{
  "openapi": "3.0.3",
  "info": {
    "title": "Dockhand API",
    "version": "1.0.41",
    "description": "Auto-generated from src/routes/**/+server.ts by scripts/generate-openapi.ts. Path, HTTP method, tag, and auth requirement are derived automatically from the route tree and hooks.server.ts PUBLIC_PATHS. Parameters, response status codes, and request body fields are additionally auto-detected from each handler’s own source (query params via url.searchParams, status codes via status:/error(), body fields via destructuring) — adding a new endpoint therefore requires ZERO manual spec edits to show up with real params/responses. An optional, additive `@openapi` JSDoc annotation on a handler replaces the generic auto-descriptions with hand-written summaries, exact types, and examples."
  },
  "servers": [
    {
      "url": "/"
    }
  ],
  "tags": [
    {
      "name": "activity"
    },
    {
      "name": "audit"
    },
    {
      "name": "auth"
    },
    {
      "name": "auto-update"
    },
    {
      "name": "backup"
    },
    {
      "name": "batch"
    },
    {
      "name": "changelog"
    },
    {
      "name": "config-sets"
    },
    {
      "name": "containers"
    },
    {
      "name": "dashboard"
    },
    {
      "name": "debug"
    },
    {
      "name": "dependencies"
    },
    {
      "name": "docs"
    },
    {
      "name": "environments"
    },
    {
      "name": "events"
    },
    {
      "name": "git"
    },
    {
      "name": "hawser"
    },
    {
      "name": "health"
    },
    {
      "name": "host"
    },
    {
      "name": "images"
    },
    {
      "name": "jobs"
    },
    {
      "name": "labels"
    },
    {
      "name": "legal"
    },
    {
      "name": "license"
    },
    {
      "name": "logs"
    },
    {
      "name": "metrics"
    },
    {
      "name": "networks"
    },
    {
      "name": "notifications"
    },
    {
      "name": "preferences"
    },
    {
      "name": "profile"
    },
    {
      "name": "prune"
    },
    {
      "name": "registries"
    },
    {
      "name": "registry"
    },
    {
      "name": "roles"
    },
    {
      "name": "schedules"
    },
    {
      "name": "self-update"
    },
    {
      "name": "settings"
    },
    {
      "name": "stacks"
    },
    {
      "name": "system"
    },
    {
      "name": "templates"
    },
    {
      "name": "users"
    },
    {
      "name": "volumes"
    },
    {
      "name": "vulnerabilities"
    }
  ],
  "components": {
    "securitySchemes": {
      "cookieAuth": {
        "type": "apiKey",
        "in": "cookie",
        "name": "dockhand_session",
        "description": "Session cookie set on login (src/lib/server/auth.ts validateSession)."
      },
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "dh_<43-char base64url>",
        "description": "User-scoped API token (src/lib/server/api-tokens.ts). Only evaluated on /api/* and /metrics when no session cookie is present (src/hooks.server.ts). Rate-limited: 10 failures/IP -> 429 for 5 minutes."
      }
    }
  },
  "security": [
    {
      "cookieAuth": []
    },
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/api/activity": {
      "get": {
        "operationId": "get_api_activity",
        "tags": [
          "activity"
        ],
        "summary": "Query container activity events with filters and pagination",
        "parameters": [
          {
            "name": "environmentId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Filter to a single environment (from GET /api/environments)"
          },
          {
            "name": "containerId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter by container ID (from GET /api/containers)"
          },
          {
            "name": "containerName",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter by container name"
          },
          {
            "name": "actions",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated event actions to filter by"
          },
          {
            "name": "labels",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated labels to filter by"
          },
          {
            "name": "fromDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Start of the date range (ISO 8601)"
          },
          {
            "name": "toDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "End of the date range (ISO 8601)"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Maximum number of events to return"
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Number of events to skip (pagination)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "events": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "containerName": {
                            "type": "string"
                          },
                          "action": {
                            "type": "string"
                          },
                          "timestamp": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "id"
                        ]
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "total",
                    "limit",
                    "offset"
                  ]
                },
                "example": {
                  "events": [
                    {
                      "id": 0,
                      "containerName": "string",
                      "action": "string",
                      "timestamp": "string"
                    }
                  ],
                  "total": 0,
                  "limit": 0,
                  "offset": 0
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (requires the activity:view permission)"
          },
          "500": {
            "description": "Failed to fetch container events"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "delete": {
        "operationId": "delete_api_activity",
        "tags": [
          "activity"
        ],
        "summary": "Clear all stored container activity events",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (requires the activity:delete permission)"
          },
          "500": {
            "description": "Failed to clear container events"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/activity/containers": {
      "get": {
        "operationId": "get_api_activity_containers",
        "tags": [
          "activity"
        ],
        "summary": "List distinct container names that appear in the activity log, for filter dropdowns",
        "parameters": [
          {
            "name": "environment_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Filter to a single environment (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "example": [
                  "web-1",
                  "db-1",
                  "cache-1"
                ]
              }
            }
          },
          "403": {
            "description": "Permission denied (requires the activity:view permission)"
          },
          "500": {
            "description": "Failed to fetch container names"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/activity/events": {
      "get": {
        "operationId": "get_api_activity_events",
        "tags": [
          "activity"
        ],
        "summary": "Stream live container activity and environment-status events over Server-Sent Events",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Server-Sent Events stream (text/event-stream) emitting connected, heartbeat, activity and env_status events"
          },
          "403": {
            "description": "Permission denied (requires the activity:view permission)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/activity/stats": {
      "get": {
        "operationId": "get_api_activity_stats",
        "tags": [
          "activity"
        ],
        "summary": "Get aggregate container activity statistics (totals and counts by action)",
        "parameters": [
          {
            "name": "environment_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Filter to a single environment (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "total": {
                      "type": "integer"
                    },
                    "today": {
                      "type": "integer"
                    },
                    "byAction": {
                      "type": "object",
                      "properties": {}
                    }
                  },
                  "required": [
                    "total",
                    "today"
                  ]
                },
                "example": {
                  "total": 128,
                  "today": 7,
                  "byAction": {
                    "start": 40,
                    "stop": 30
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (requires the activity:view permission)"
          },
          "500": {
            "description": "Failed to fetch stats"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/audit": {
      "get": {
        "operationId": "get_api_audit",
        "tags": [
          "audit"
        ],
        "summary": "Query the audit log with filters and pagination (Enterprise only)",
        "parameters": [
          {
            "name": "usernames",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated usernames to filter by"
          },
          {
            "name": "entityTypes",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated entity types to filter by"
          },
          {
            "name": "actions",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated actions to filter by"
          },
          {
            "name": "username",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Legacy single-username filter"
          },
          {
            "name": "entityType",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Legacy single entity-type filter"
          },
          {
            "name": "action",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Legacy single-action filter"
          },
          {
            "name": "environmentId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Filter to a single environment (from GET /api/environments)"
          },
          {
            "name": "labels",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated labels to filter by"
          },
          {
            "name": "fromDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Start of the date range (ISO 8601)"
          },
          {
            "name": "toDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "End of the date range (ISO 8601)"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Maximum number of entries to return"
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Number of entries to skip (pagination)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "logs": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "username": {
                            "type": "string"
                          },
                          "action": {
                            "type": "string"
                          },
                          "entityType": {
                            "type": "string"
                          },
                          "entityName": {
                            "type": "string"
                          },
                          "createdAt": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "id"
                        ]
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "logs": [
                    {
                      "id": 0,
                      "username": "string",
                      "action": "string",
                      "entityType": "string",
                      "entityName": "string",
                      "createdAt": "string"
                    }
                  ],
                  "total": 0
                }
              }
            }
          },
          "403": {
            "description": "Enterprise required, or permission denied"
          },
          "500": {
            "description": "Failed to fetch audit logs"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/audit/events": {
      "get": {
        "operationId": "get_api_audit_events",
        "tags": [
          "audit"
        ],
        "summary": "Stream live audit-log events over Server-Sent Events (Enterprise only)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Server-Sent Events stream (text/event-stream) emitting connected, heartbeat and audit events"
          },
          "403": {
            "description": "Enterprise required, or permission denied"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/audit/export": {
      "get": {
        "operationId": "get_api_audit_export",
        "tags": [
          "audit"
        ],
        "summary": "Export the audit log as a downloadable file in JSON, CSV or Markdown (Enterprise only)",
        "parameters": [
          {
            "name": "username",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter by username"
          },
          {
            "name": "entityType",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter by entity type"
          },
          {
            "name": "action",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter by action"
          },
          {
            "name": "environmentId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Filter to a single environment (from GET /api/environments)"
          },
          {
            "name": "fromDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Start of the date range (ISO 8601)"
          },
          {
            "name": "toDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "End of the date range (ISO 8601)"
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Output format — json (default), csv or md"
          }
        ],
        "responses": {
          "200": {
            "description": "Downloadable audit-log export (Content-Disposition attachment; JSON, CSV or Markdown)"
          },
          "403": {
            "description": "Enterprise required, or permission denied"
          },
          "500": {
            "description": "Failed to export audit logs"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/audit/users": {
      "get": {
        "operationId": "get_api_audit_users",
        "tags": [
          "audit"
        ],
        "summary": "List the distinct usernames that appear in the audit log, for filter dropdowns (Enterprise only)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "example": [
                  "admin",
                  "ci-bot",
                  "alice"
                ]
              }
            }
          },
          "403": {
            "description": "Enterprise required, or permission denied"
          },
          "500": {
            "description": "Failed to fetch audit log users"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/auth/ldap": {
      "get": {
        "operationId": "get_api_auth_ldap",
        "tags": [
          "auth"
        ],
        "summary": "List all configured LDAP providers (enterprise only; bind passwords are masked)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      },
                      "name": {
                        "type": "string"
                      },
                      "enabled": {
                        "type": "boolean"
                      },
                      "serverUrl": {
                        "type": "string"
                      },
                      "baseDn": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "enabled",
                      "serverUrl",
                      "baseDn"
                    ]
                  }
                },
                "example": [
                  {
                    "id": 1,
                    "name": "Corporate LDAP",
                    "enabled": true,
                    "serverUrl": "ldaps://ldap.example.com:636",
                    "baseDn": "dc=example,dc=com"
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Authentication required (auth is enabled and the caller is not an authenticated admin)"
          },
          "403": {
            "description": "Enterprise license required"
          },
          "500": {
            "description": "Failed to read the LDAP configurations"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_auth_ldap",
        "tags": [
          "auth"
        ],
        "summary": "Create a new LDAP provider configuration (enterprise only)",
        "parameters": [],
        "responses": {
          "201": {
            "description": "LDAP configuration created (bindPassword is masked in the response)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "serverUrl": {
                      "type": "string"
                    },
                    "baseDn": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "enabled",
                    "serverUrl",
                    "baseDn"
                  ]
                },
                "example": {
                  "id": 0,
                  "name": "string",
                  "enabled": true,
                  "serverUrl": "string",
                  "baseDn": "string"
                }
              }
            }
          },
          "400": {
            "description": "Name, server URL, and base DN are required"
          },
          "401": {
            "description": "Authentication required (auth is enabled and the caller is not an authenticated admin)"
          },
          "403": {
            "description": "Enterprise license required"
          },
          "500": {
            "description": "Failed to create the LDAP configuration"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "serverUrl": {
                    "type": "string"
                  },
                  "baseDn": {
                    "type": "string"
                  },
                  "enabled": {
                    "type": "boolean"
                  },
                  "bindDn": {
                    "type": "string"
                  },
                  "bindPassword": {
                    "type": "string"
                  },
                  "userFilter": {
                    "type": "string"
                  },
                  "usernameAttribute": {
                    "type": "string"
                  },
                  "emailAttribute": {
                    "type": "string"
                  },
                  "displayNameAttribute": {
                    "type": "string"
                  },
                  "groupBaseDn": {
                    "type": "string"
                  },
                  "groupFilter": {
                    "type": "string"
                  },
                  "adminGroup": {
                    "type": "string"
                  },
                  "roleMappings": {
                    "type": "object",
                    "properties": {}
                  },
                  "tlsEnabled": {
                    "type": "boolean"
                  },
                  "tlsCa": {
                    "type": "string"
                  }
                },
                "required": [
                  "name",
                  "serverUrl",
                  "baseDn"
                ]
              },
              "example": {
                "name": "Corporate LDAP",
                "serverUrl": "ldaps://ldap.example.com:636",
                "baseDn": "dc=example,dc=com",
                "bindDn": "cn=admin,dc=example,dc=com",
                "bindPassword": "***",
                "enabled": true,
                "tlsEnabled": true
              }
            }
          }
        }
      }
    },
    "/api/auth/ldap/{id}": {
      "get": {
        "operationId": "get_api_auth_ldap_id",
        "tags": [
          "auth"
        ],
        "summary": "Get a single LDAP provider configuration by id (enterprise only; bind password is masked)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric id of the LDAP configuration (from GET /api/auth/ldap)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "serverUrl": {
                      "type": "string"
                    },
                    "baseDn": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "enabled",
                    "serverUrl",
                    "baseDn"
                  ]
                },
                "example": {
                  "id": 0,
                  "name": "string",
                  "enabled": true,
                  "serverUrl": "string",
                  "baseDn": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid id (not a number)"
          },
          "401": {
            "description": "Authentication required (auth is enabled and the caller is not an authenticated admin)"
          },
          "403": {
            "description": "Enterprise license required"
          },
          "404": {
            "description": "LDAP configuration not found"
          },
          "500": {
            "description": "Failed to read the LDAP configuration"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "operationId": "put_api_auth_ldap_id",
        "tags": [
          "auth"
        ],
        "summary": "Update an existing LDAP provider configuration (only supplied fields change; a masked bindPassword is ignored)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric id of the LDAP configuration (from GET /api/auth/ldap)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "serverUrl": {
                      "type": "string"
                    },
                    "baseDn": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "enabled",
                    "serverUrl",
                    "baseDn"
                  ]
                },
                "example": {
                  "id": 0,
                  "name": "string",
                  "enabled": true,
                  "serverUrl": "string",
                  "baseDn": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid id (not a number)"
          },
          "401": {
            "description": "Authentication required (auth is enabled and the caller is not an authenticated admin)"
          },
          "403": {
            "description": "Enterprise license required"
          },
          "404": {
            "description": "LDAP configuration not found"
          },
          "500": {
            "description": "Failed to update the LDAP configuration"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "enabled": {
                    "type": "boolean"
                  },
                  "serverUrl": {
                    "type": "string"
                  },
                  "bindDn": {
                    "type": "string"
                  },
                  "bindPassword": {
                    "type": "string"
                  },
                  "baseDn": {
                    "type": "string"
                  },
                  "userFilter": {
                    "type": "string"
                  },
                  "usernameAttribute": {
                    "type": "string"
                  },
                  "emailAttribute": {
                    "type": "string"
                  },
                  "displayNameAttribute": {
                    "type": "string"
                  },
                  "groupBaseDn": {
                    "type": "string"
                  },
                  "groupFilter": {
                    "type": "string"
                  },
                  "adminGroup": {
                    "type": "string"
                  },
                  "roleMappings": {
                    "type": "object",
                    "properties": {}
                  },
                  "tlsEnabled": {
                    "type": "boolean"
                  },
                  "tlsCa": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "enabled": false,
                "userFilter": "(sAMAccountName={{username}})",
                "bindPassword": "***"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_auth_ldap_id",
        "tags": [
          "auth"
        ],
        "summary": "Delete an LDAP provider configuration by id (enterprise only)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric id of the LDAP configuration (from GET /api/auth/ldap)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid id (not a number)"
          },
          "401": {
            "description": "Authentication required (auth is enabled and the caller is not an authenticated admin)"
          },
          "403": {
            "description": "Enterprise license required"
          },
          "404": {
            "description": "LDAP configuration not found"
          },
          "500": {
            "description": "Failed to delete the LDAP configuration"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/auth/ldap/{id}/test": {
      "post": {
        "operationId": "post_api_auth_ldap_id_test",
        "tags": [
          "auth"
        ],
        "summary": "Test connectivity of a stored LDAP configuration by id (enterprise only)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric id of the LDAP configuration to test (from GET /api/auth/ldap)"
          }
        ],
        "responses": {
          "200": {
            "description": "Connection test result (success flag plus diagnostic detail from the LDAP server)"
          },
          "400": {
            "description": "Invalid id (not a number)"
          },
          "401": {
            "description": "Authentication required (auth is enabled and the caller is not an authenticated admin)"
          },
          "403": {
            "description": "Enterprise license required"
          },
          "404": {
            "description": "LDAP configuration not found"
          },
          "500": {
            "description": "Failed to test the LDAP connection"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/auth/login": {
      "post": {
        "operationId": "post_api_auth_login",
        "tags": [
          "auth"
        ],
        "summary": "Authenticate with username/password (local or LDAP) and set the session cookie",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Login succeeded and dockhand_session cookie was set — OR requiresMfa:true if a second factor is needed first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "user": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "username": {
                          "type": "string"
                        },
                        "email": {
                          "type": "string"
                        },
                        "displayName": {
                          "type": "string"
                        },
                        "isAdmin": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "id",
                        "username",
                        "isAdmin"
                      ]
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true,
                  "user": {
                    "id": 1,
                    "username": "admin",
                    "email": "admin@example.com",
                    "displayName": "Admin",
                    "isAdmin": true
                  }
                }
              }
            }
          },
          "400": {
            "description": "Authentication disabled, or username/password missing"
          },
          "401": {
            "description": "Invalid credentials or invalid MFA code"
          },
          "403": {
            "description": "Local login disabled via DISABLE_LOCAL_LOGIN"
          },
          "429": {
            "description": "Rate-limited (too many attempts for this IP+username)"
          },
          "500": {
            "description": "Unexpected error during authentication"
          }
        },
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "username": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  },
                  "mfaToken": {
                    "type": "string"
                  },
                  "provider": {
                    "type": "string"
                  }
                },
                "required": [
                  "username",
                  "password"
                ]
              },
              "example": {
                "username": "admin",
                "password": "correct horse battery staple",
                "provider": "local"
              }
            }
          }
        }
      }
    },
    "/api/auth/logout": {
      "post": {
        "operationId": "post_api_auth_logout",
        "tags": [
          "auth"
        ],
        "summary": "Destroy the current session (clears the dockhand_session cookie)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error while destroying the session"
          }
        },
        "security": []
      }
    },
    "/api/auth/oidc": {
      "get": {
        "operationId": "get_api_auth_oidc",
        "tags": [
          "auth"
        ],
        "summary": "List all configured OIDC providers (client secrets are masked)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      },
                      "name": {
                        "type": "string"
                      },
                      "enabled": {
                        "type": "boolean"
                      },
                      "issuerUrl": {
                        "type": "string"
                      },
                      "clientId": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "enabled",
                      "issuerUrl",
                      "clientId"
                    ]
                  }
                },
                "example": [
                  {
                    "id": 1,
                    "name": "Authentik",
                    "enabled": true,
                    "issuerUrl": "https://auth.example.com/application/o/dockhand/",
                    "clientId": "dockhand"
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Authentication required (auth is enabled and the caller is not authenticated)"
          },
          "403": {
            "description": "Permission denied (missing settings:view)"
          },
          "500": {
            "description": "Failed to read the OIDC configurations"
          }
        },
        "security": []
      },
      "post": {
        "operationId": "post_api_auth_oidc",
        "tags": [
          "auth"
        ],
        "summary": "Create a new OIDC provider configuration",
        "parameters": [],
        "responses": {
          "201": {
            "description": "OIDC configuration created (clientSecret is masked in the response)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "issuerUrl": {
                      "type": "string"
                    },
                    "clientId": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "enabled",
                    "issuerUrl",
                    "clientId"
                  ]
                },
                "example": {
                  "id": 0,
                  "name": "string",
                  "enabled": true,
                  "issuerUrl": "string",
                  "clientId": "string"
                }
              }
            }
          },
          "400": {
            "description": "A required field is missing (name, issuerUrl, clientId, clientSecret, redirectUri)"
          },
          "401": {
            "description": "Authentication required (auth is enabled and the caller is not authenticated)"
          },
          "403": {
            "description": "Permission denied (missing settings:edit)"
          },
          "500": {
            "description": "Failed to create the OIDC configuration"
          }
        },
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "issuerUrl": {
                    "type": "string"
                  },
                  "clientId": {
                    "type": "string"
                  },
                  "clientSecret": {
                    "type": "string"
                  },
                  "redirectUri": {
                    "type": "string"
                  },
                  "enabled": {
                    "type": "boolean"
                  },
                  "scopes": {
                    "type": "string"
                  },
                  "usernameClaim": {
                    "type": "string"
                  },
                  "emailClaim": {
                    "type": "string"
                  },
                  "displayNameClaim": {
                    "type": "string"
                  },
                  "adminClaim": {
                    "type": "string"
                  },
                  "adminValue": {
                    "type": "string"
                  },
                  "roleMappingsClaim": {
                    "type": "string"
                  },
                  "roleMappings": {
                    "type": "object",
                    "properties": {}
                  }
                },
                "required": [
                  "name",
                  "issuerUrl",
                  "clientId",
                  "clientSecret",
                  "redirectUri"
                ]
              },
              "example": {
                "name": "Authentik",
                "issuerUrl": "https://auth.example.com/application/o/dockhand/",
                "clientId": "dockhand",
                "clientSecret": "***",
                "redirectUri": "https://dockhand.example.com/api/auth/oidc/callback",
                "enabled": true,
                "scopes": "openid profile email"
              }
            }
          }
        }
      }
    },
    "/api/auth/oidc/{id}": {
      "get": {
        "operationId": "get_api_auth_oidc_id",
        "tags": [
          "auth"
        ],
        "summary": "Get a single OIDC provider configuration by id (client secret is masked)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric id of the OIDC configuration (from GET /api/auth/oidc)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "issuerUrl": {
                      "type": "string"
                    },
                    "clientId": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "enabled",
                    "issuerUrl",
                    "clientId"
                  ]
                },
                "example": {
                  "id": 0,
                  "name": "string",
                  "enabled": true,
                  "issuerUrl": "string",
                  "clientId": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid configuration id (not a number)"
          },
          "401": {
            "description": "Authentication required (auth is enabled and the caller is not authenticated)"
          },
          "403": {
            "description": "Permission denied (missing settings:view)"
          },
          "404": {
            "description": "OIDC configuration not found"
          },
          "500": {
            "description": "Failed to read the OIDC configuration"
          }
        },
        "security": []
      },
      "put": {
        "operationId": "put_api_auth_oidc_id",
        "tags": [
          "auth"
        ],
        "summary": "Update an existing OIDC provider configuration (only supplied fields change; a masked clientSecret is ignored)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric id of the OIDC configuration (from GET /api/auth/oidc)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "issuerUrl": {
                      "type": "string"
                    },
                    "clientId": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "enabled",
                    "issuerUrl",
                    "clientId"
                  ]
                },
                "example": {
                  "id": 0,
                  "name": "string",
                  "enabled": true,
                  "issuerUrl": "string",
                  "clientId": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid configuration id (not a number)"
          },
          "401": {
            "description": "Authentication required (auth is enabled and the caller is not authenticated)"
          },
          "403": {
            "description": "Permission denied (missing settings:edit)"
          },
          "404": {
            "description": "OIDC configuration not found"
          },
          "500": {
            "description": "Failed to update the OIDC configuration"
          }
        },
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "enabled": {
                    "type": "boolean"
                  },
                  "issuerUrl": {
                    "type": "string"
                  },
                  "clientId": {
                    "type": "string"
                  },
                  "clientSecret": {
                    "type": "string"
                  },
                  "redirectUri": {
                    "type": "string"
                  },
                  "scopes": {
                    "type": "string"
                  },
                  "usernameClaim": {
                    "type": "string"
                  },
                  "emailClaim": {
                    "type": "string"
                  },
                  "displayNameClaim": {
                    "type": "string"
                  },
                  "adminClaim": {
                    "type": "string"
                  },
                  "adminValue": {
                    "type": "string"
                  },
                  "roleMappingsClaim": {
                    "type": "string"
                  },
                  "roleMappings": {
                    "type": "object",
                    "properties": {}
                  }
                }
              },
              "example": {
                "enabled": true,
                "scopes": "openid profile email groups",
                "clientSecret": "***"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_auth_oidc_id",
        "tags": [
          "auth"
        ],
        "summary": "Delete an OIDC provider configuration by id",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric id of the OIDC configuration (from GET /api/auth/oidc)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid configuration id (not a number)"
          },
          "401": {
            "description": "Authentication required (auth is enabled and the caller is not authenticated)"
          },
          "403": {
            "description": "Permission denied (missing settings:edit)"
          },
          "404": {
            "description": "OIDC configuration not found"
          },
          "500": {
            "description": "Failed to delete the OIDC configuration"
          }
        },
        "security": []
      }
    },
    "/api/auth/oidc/{id}/initiate": {
      "get": {
        "operationId": "get_api_auth_oidc_id_initiate",
        "tags": [
          "auth"
        ],
        "summary": "Start the OIDC login flow for a provider — on success throws a 302 redirect to the IdP authorization URL",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric id of the OIDC provider (from GET /api/auth/oidc)"
          },
          {
            "name": "redirect",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Post-login destination path to return to (defaults to /)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "302": {
            "description": "Redirect to the IdP's authorization URL"
          },
          "400": {
            "description": "Authentication is not enabled, or the configuration id is invalid"
          },
          "404": {
            "description": "OIDC provider not found or disabled"
          },
          "500": {
            "description": "Failed to build the authorization URL / initiate SSO"
          }
        },
        "security": []
      },
      "post": {
        "operationId": "post_api_auth_oidc_id_initiate",
        "tags": [
          "auth"
        ],
        "summary": "Return the OIDC authorization URL for a provider without redirecting (JSON response)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric id of the OIDC provider (from GET /api/auth/oidc)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "url"
                  ]
                },
                "example": {
                  "url": "string"
                }
              }
            }
          },
          "400": {
            "description": "Authentication is not enabled, or the configuration id is invalid"
          },
          "404": {
            "description": "OIDC provider not found or disabled"
          },
          "500": {
            "description": "Failed to build the authorization URL / initiate SSO"
          }
        },
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "redirect": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "redirect": "/dashboard"
              }
            }
          }
        }
      }
    },
    "/api/auth/oidc/{id}/test": {
      "post": {
        "operationId": "post_api_auth_oidc_id_test",
        "tags": [
          "auth"
        ],
        "summary": "Test the discovery/connection of a stored OIDC configuration by id",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric id of the OIDC configuration to test (from GET /api/auth/oidc)"
          }
        ],
        "responses": {
          "200": {
            "description": "Connection test result (success flag plus diagnostic detail from the provider)"
          },
          "400": {
            "description": "Invalid configuration id (not a number)"
          },
          "403": {
            "description": "Admin access required (auth is enabled and the caller is not an admin)"
          },
          "500": {
            "description": "Failed to test the OIDC connection"
          }
        },
        "security": []
      }
    },
    "/api/auth/oidc/callback": {
      "get": {
        "operationId": "get_api_auth_oidc_callback",
        "tags": [
          "auth"
        ],
        "summary": "Handle the OIDC redirect callback from the IdP — always responds with a 302 redirect (to the original destination on success, or to /login with an error query param on failure)",
        "parameters": [
          {
            "name": "code",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Authorization code returned by the IdP"
          },
          {
            "name": "state",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Opaque state value used to correlate the request and carry the post-login redirect"
          },
          {
            "name": "error",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Error code returned by the IdP when authentication failed"
          },
          {
            "name": "error_description",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Human-readable error detail returned by the IdP"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "302": {
            "description": "Always a redirect — on success a session cookie is set and the caller is redirected to the original destination; on any error the caller is redirected to /login with an error query param"
          }
        },
        "security": []
      }
    },
    "/api/auth/providers": {
      "get": {
        "operationId": "get_api_auth_providers",
        "tags": [
          "auth"
        ],
        "summary": "List the authentication providers offered on the login page (local, LDAP, OIDC), plus the default provider",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "providers": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string"
                          },
                          "initiateUrl": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "id",
                          "name",
                          "type"
                        ]
                      }
                    },
                    "defaultProvider": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "providers": [
                    {
                      "id": "local",
                      "name": "Local",
                      "type": "local"
                    },
                    {
                      "id": "oidc:1",
                      "name": "Authentik",
                      "type": "oidc",
                      "initiateUrl": "/api/auth/oidc/1/initiate"
                    }
                  ],
                  "defaultProvider": "local"
                }
              }
            }
          }
        },
        "security": []
      }
    },
    "/api/auth/session": {
      "get": {
        "operationId": "get_api_auth_session",
        "tags": [
          "auth"
        ],
        "summary": "Get the current session (public — used by the frontend to bootstrap auth state)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "user is present only when authenticated:true",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "authenticated": {
                      "type": "boolean"
                    },
                    "authEnabled": {
                      "type": "boolean"
                    },
                    "user": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "username": {
                          "type": "string"
                        },
                        "email": {
                          "type": "string"
                        },
                        "displayName": {
                          "type": "string"
                        },
                        "avatar": {
                          "type": "string"
                        },
                        "isAdmin": {
                          "type": "boolean"
                        },
                        "provider": {
                          "type": "string"
                        }
                      }
                    }
                  },
                  "required": [
                    "authenticated",
                    "authEnabled"
                  ]
                },
                "example": {
                  "authenticated": true,
                  "authEnabled": true,
                  "user": {
                    "id": 1,
                    "username": "admin",
                    "email": "admin@example.com",
                    "displayName": "Admin",
                    "avatar": null,
                    "isAdmin": true,
                    "provider": "local"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error while validating the session"
          }
        },
        "security": []
      }
    },
    "/api/auth/settings": {
      "get": {
        "operationId": "get_api_auth_settings",
        "tags": [
          "auth"
        ],
        "summary": "Get the global authentication settings (whether auth is enabled and the default provider)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "authEnabled": {
                      "type": "boolean"
                    },
                    "defaultProvider": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "authEnabled"
                  ]
                },
                "example": {
                  "authEnabled": true,
                  "defaultProvider": "local"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required (auth is enabled and the caller is not authenticated)"
          },
          "403": {
            "description": "Permission denied (missing settings:view)"
          },
          "500": {
            "description": "Failed to read the auth settings"
          }
        },
        "security": []
      },
      "put": {
        "operationId": "put_api_auth_settings",
        "tags": [
          "auth"
        ],
        "summary": "Update the global authentication settings (enabling auth requires at least one admin/user to exist)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "authEnabled": {
                      "type": "boolean"
                    },
                    "defaultProvider": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "authEnabled"
                  ]
                },
                "example": {
                  "authEnabled": true,
                  "defaultProvider": "string"
                }
              }
            }
          },
          "400": {
            "description": "Cannot enable authentication without an existing user/admin (response includes requiresUser:true)"
          },
          "401": {
            "description": "Authentication required (auth is enabled and the caller is not authenticated)"
          },
          "403": {
            "description": "Permission denied (missing settings:edit)"
          },
          "500": {
            "description": "Failed to update the auth settings"
          }
        },
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "authEnabled": {
                    "type": "boolean"
                  },
                  "defaultProvider": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "authEnabled": true,
                "defaultProvider": "local"
              }
            }
          }
        }
      }
    },
    "/api/auth/tokens": {
      "get": {
        "operationId": "get_api_auth_tokens",
        "tags": [
          "auth"
        ],
        "summary": "List the authenticated user's API tokens (never returns the hash/secret)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      },
                      "name": {
                        "type": "string"
                      },
                      "tokenPrefix": {
                        "type": "string"
                      },
                      "lastUsed": {
                        "type": "string"
                      },
                      "expiresAt": {
                        "type": "string"
                      },
                      "createdAt": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "tokenPrefix",
                      "createdAt"
                    ]
                  }
                },
                "example": [
                  {
                    "id": 1,
                    "name": "CI/CD Pipeline",
                    "tokenPrefix": "a1b2c3d4",
                    "lastUsed": "2027-01-02T08:00:00Z",
                    "expiresAt": "2027-01-01T00:00:00Z",
                    "createdAt": "2026-06-01T10:00:00Z"
                  }
                ]
              }
            }
          },
          "400": {
            "description": "Authentication is not enabled"
          },
          "401": {
            "description": "Not authenticated"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_auth_tokens",
        "tags": [
          "auth"
        ],
        "summary": "Create a new API token for the authenticated user (requires a session, not a Bearer token)",
        "parameters": [],
        "responses": {
          "201": {
            "description": "Token created — the plaintext value is shown ONLY this once and cannot be retrieved again",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string"
                    },
                    "id": {
                      "type": "integer"
                    },
                    "tokenPrefix": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "token",
                    "id",
                    "tokenPrefix"
                  ]
                },
                "example": {
                  "token": "dh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
                  "id": 42,
                  "tokenPrefix": "a1b2c3d4"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body, missing password for local users, or MAX_TOKENS_PER_USER (25) reached"
          },
          "401": {
            "description": "Not authenticated"
          },
          "403": {
            "description": "Token creation attempted via Bearer auth (session login required) or wrong password"
          },
          "404": {
            "description": "User record not found while confirming the password (local provider)"
          },
          "429": {
            "description": "Too many failed password confirmation attempts (rate limited)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "expiresAt": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              },
              "example": {
                "name": "CI/CD Pipeline",
                "expiresAt": "2027-01-01T00:00:00Z",
                "password": "correct horse battery staple"
              }
            }
          }
        }
      }
    },
    "/api/auth/tokens/{id}": {
      "delete": {
        "operationId": "delete_api_auth_tokens_id",
        "tags": [
          "auth"
        ],
        "summary": "Revoke (permanently delete) one of the authenticated user's API tokens",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Token id (from GET /api/auth/tokens)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Authentication not enabled, or id is not a valid integer"
          },
          "401": {
            "description": "Not authenticated"
          },
          "403": {
            "description": "Attempted via Bearer auth (a leaked token cannot revoke other tokens — session required)"
          },
          "404": {
            "description": "Token not found, or belongs to a different (non-admin) user"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/auto-update": {
      "get": {
        "operationId": "get_api_auto-update",
        "tags": [
          "auto-update"
        ],
        "summary": "Get all enabled auto-update settings for an environment, keyed by container name",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID to read settings for (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {}
                },
                "example": {
                  "web-1": {
                    "enabled": true,
                    "scheduleType": "daily",
                    "cronExpression": "0 3 * * *",
                    "vulnerabilityCriteria": "never"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Failed to get auto-update settings"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/auto-update/{containerName}": {
      "get": {
        "operationId": "get_api_auto-update_containerName",
        "tags": [
          "auto-update"
        ],
        "summary": "Get the auto-update setting for a container (returns sensible defaults when none is stored)",
        "parameters": [
          {
            "name": "containerName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container name (URL-encoded)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment the container belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enabled": {
                      "type": "boolean"
                    },
                    "scheduleType": {
                      "type": "string"
                    },
                    "cronExpression": {
                      "type": "string"
                    },
                    "vulnerabilityCriteria": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "enabled",
                    "scheduleType",
                    "vulnerabilityCriteria"
                  ]
                },
                "example": {
                  "enabled": false,
                  "scheduleType": "daily",
                  "cronExpression": "0 3 * * *",
                  "vulnerabilityCriteria": "never"
                }
              }
            }
          },
          "500": {
            "description": "Failed to get auto-update setting"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_auto-update_containerName",
        "tags": [
          "auto-update"
        ],
        "summary": "Create or update a container's auto-update setting; enabled=false deletes it and its schedule",
        "parameters": [
          {
            "name": "containerName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container name (URL-encoded)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment the container belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "scheduleType": {
                      "type": "string"
                    },
                    "cronExpression": {
                      "type": "string"
                    },
                    "vulnerabilityCriteria": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "boolean"
                    },
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "id",
                    "enabled",
                    "scheduleType",
                    "vulnerabilityCriteria"
                  ]
                },
                "example": {
                  "success": true,
                  "deleted": true
                }
              }
            }
          },
          "500": {
            "description": "Failed to save auto-update setting"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "cronExpression": {
                    "type": "string"
                  },
                  "cron_expression": {
                    "type": "string"
                  },
                  "vulnerabilityCriteria": {
                    "type": "string"
                  },
                  "vulnerability_criteria": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "enabled": true,
                "cronExpression": "0 3 * * *",
                "vulnerabilityCriteria": "never"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_auto-update_containerName",
        "tags": [
          "auto-update"
        ],
        "summary": "Delete a container's auto-update setting and unregister its schedule",
        "parameters": [
          {
            "name": "containerName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container name (URL-encoded)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment the container belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "500": {
            "description": "Failed to delete auto-update setting"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/backup/configs": {
      "get": {
        "operationId": "get_api_backup_configs",
        "tags": [
          "backup"
        ],
        "summary": "List backup configurations, optionally filtered by type, target and environment (enterprise callers only see configs for environments they can access)",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter by backup config type (e.g. \"container\", \"stack\")"
          },
          {
            "name": "target",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter by the backed-up target name (container or stack name)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Filter by environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Array of backup configuration objects"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Permission denial (403, \"backups:view\") is produced by the shared requireBackups route guard."
      },
      "post": {
        "operationId": "post_api_backup_configs",
        "tags": [
          "backup"
        ],
        "summary": "Create a backup configuration for a container or stack, optionally scheduled, and register its cron schedule when enabled",
        "parameters": [],
        "responses": {
          "201": {
            "description": "The created backup configuration object"
          },
          "400": {
            "description": "Invalid input — missing destinationId/targetName, invalid targetName, invalid cron expression, invalid retention, or a local repository paired with a remote environment / non-backupable stack"
          },
          "403": {
            "description": "Access denied to the requested environment (enterprise)"
          },
          "500": {
            "description": "Failed to create the backup configuration (persistence error)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Permission denial (403, \"backups:manage\") is produced by the shared requireBackups route guard. A requested environmentId the caller can't access also 403s (enterprise). destinationId from GET /api/backup/destinations. environmentId from GET /api/environments.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "destinationId": {
                    "type": "integer"
                  },
                  "targetName": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string"
                  },
                  "environmentId": {
                    "type": "integer"
                  },
                  "enabled": {
                    "type": "boolean"
                  },
                  "allVolumes": {
                    "type": "boolean"
                  },
                  "selectedVolumes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "stopBeforeBackup": {
                    "type": "boolean"
                  },
                  "schedule": {
                    "type": "string"
                  },
                  "retention": {
                    "type": "object",
                    "properties": {
                      "keepLast": {
                        "type": "integer"
                      },
                      "keepDaily": {
                        "type": "integer"
                      },
                      "keepWeekly": {
                        "type": "integer"
                      },
                      "keepMonthly": {
                        "type": "integer"
                      },
                      "keepYearly": {
                        "type": "integer"
                      }
                    }
                  },
                  "options": {
                    "type": "object",
                    "properties": {}
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "destinationId",
                  "targetName"
                ]
              },
              "example": {
                "destinationId": 3,
                "targetName": "nextcloud",
                "type": "container",
                "environmentId": 1,
                "enabled": true,
                "allVolumes": true,
                "stopBeforeBackup": false,
                "schedule": "0 3 * * *",
                "retention": {
                  "keepDaily": 7,
                  "keepWeekly": 4
                }
              }
            }
          }
        }
      }
    },
    "/api/backup/configs/{id}": {
      "get": {
        "operationId": "get_api_backup_configs_id",
        "tags": [
          "backup"
        ],
        "summary": "Fetch a single backup configuration by id, enforcing environment-scoped access",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Backup configuration id (from GET /api/backup/configs)"
          }
        ],
        "responses": {
          "200": {
            "description": "The backup configuration object"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Permission (\"backups:view\") and environment-access denials (403) and not-found (404) are produced by the shared route guards."
      },
      "put": {
        "operationId": "put_api_backup_configs_id",
        "tags": [
          "backup"
        ],
        "summary": "Update a backup configuration and re-register or remove its cron schedule accordingly",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Backup configuration id (from GET /api/backup/configs)"
          }
        ],
        "responses": {
          "200": {
            "description": "The updated backup configuration object"
          },
          "400": {
            "description": "Invalid input — invalid cron expression, invalid retention, or a local repository paired with a remote environment"
          },
          "409": {
            "description": "Cannot change the destination while a backup is running for this config"
          },
          "500": {
            "description": "Update failed (persistence error)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Permission (\"backups:manage\") and environment-access denials (403) and not-found (404) are produced by the shared route guards. The environment is fixed at creation and cannot be changed here. destinationId from GET /api/backup/destinations.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "destinationId": {
                    "type": "integer"
                  },
                  "enabled": {
                    "type": "boolean"
                  },
                  "allVolumes": {
                    "type": "boolean"
                  },
                  "selectedVolumes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "stopBeforeBackup": {
                    "type": "boolean"
                  },
                  "schedule": {
                    "type": "string"
                  },
                  "retention": {
                    "type": "object",
                    "properties": {
                      "keepLast": {
                        "type": "integer"
                      },
                      "keepDaily": {
                        "type": "integer"
                      },
                      "keepWeekly": {
                        "type": "integer"
                      },
                      "keepMonthly": {
                        "type": "integer"
                      },
                      "keepYearly": {
                        "type": "integer"
                      }
                    }
                  },
                  "options": {
                    "type": "object",
                    "properties": {}
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "example": {
                "schedule": "0 4 * * *",
                "enabled": true,
                "stopBeforeBackup": true,
                "retention": {
                  "keepDaily": 14
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_backup_configs_id",
        "tags": [
          "backup"
        ],
        "summary": "Delete a backup configuration and unregister its schedule",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Backup configuration id (from GET /api/backup/configs)"
          }
        ],
        "responses": {
          "200": {
            "description": "Returns { success: true } once the configuration is deleted"
          },
          "409": {
            "description": "A backup is currently running for this config — stop it before deleting"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Permission (\"backups:manage\") and environment-access denials (403) and not-found (404) are produced by the shared route guards."
      }
    },
    "/api/backup/configs/{id}/run": {
      "post": {
        "operationId": "post_api_backup_configs_id_run",
        "tags": [
          "backup"
        ],
        "summary": "Trigger a manual backup run for a configuration, streaming progress as a Server-Sent Events job",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Backup configuration id (from GET /api/backup/configs)"
          }
        ],
        "responses": {
          "200": {
            "description": "Server-Sent Events stream of progress and a final result event (status \"success\", \"warning\", \"skipped\" or \"error\")"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Returns a text/event-stream that emits `progress` events during the run and a final `result` event. Permission (\"backups:manage\") and environment-access denials (403) and not-found (404) are produced by the shared route guards."
      }
    },
    "/api/backup/configs/{id}/stop": {
      "post": {
        "operationId": "post_api_backup_configs_id_stop",
        "tags": [
          "backup"
        ],
        "summary": "Cancel the in-flight backup for a configuration",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Backup configuration id (from GET /api/backup/configs)"
          }
        ],
        "responses": {
          "200": {
            "description": "Returns { success: true, stopped } where \"stopped\" indicates whether a running backup helper was actually killed"
          },
          "500": {
            "description": "Failed to cancel the backup (internal error)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Permission (\"backups:manage\") and environment-access denials (403) and not-found (404) are produced by the shared route guards."
      }
    },
    "/api/backup/destinations": {
      "get": {
        "operationId": "get_api_backup_destinations",
        "tags": [
          "backup"
        ],
        "summary": "List all backup destinations (restic repositories); the password is stripped and cloud-credential env vars are omitted from the list view",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Array of backup destination objects without secrets (no password, no envVars)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Permission denial (403, \"backups:view\") is produced by the shared requireBackups route guard."
      },
      "post": {
        "operationId": "post_api_backup_destinations",
        "tags": [
          "backup"
        ],
        "summary": "Create a restic backup destination, auto-initialize and test the repository, and register its default maintenance schedules",
        "parameters": [],
        "responses": {
          "201": {
            "description": "The created backup destination object (includes decrypted envVars since the caller just supplied them; password is stripped)"
          },
          "400": {
            "description": "Invalid input — missing name/repository/password, unsupported/SSRF-blocked repository, invalid restic flags, or an invalid cron schedule in the policies"
          },
          "409": {
            "description": "A destination with this name already exists"
          },
          "500": {
            "description": "Failed to create the destination (persistence error)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Permission denial (403, \"backups:manage\") is produced by the shared requireBackups route guard.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "repository": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  },
                  "envVars": {
                    "type": "object",
                    "properties": {}
                  },
                  "flags": {
                    "type": "string"
                  },
                  "hostPath": {
                    "type": "string"
                  },
                  "policies": {
                    "type": "string"
                  }
                },
                "required": [
                  "name",
                  "repository",
                  "password"
                ]
              },
              "example": {
                "name": "S3 Offsite",
                "repository": "s3:s3.amazonaws.com/my-bucket/restic",
                "password": "***",
                "envVars": {
                  "AWS_ACCESS_KEY_ID": "***",
                  "AWS_SECRET_ACCESS_KEY": "***"
                }
              }
            }
          }
        }
      }
    },
    "/api/backup/destinations/{id}": {
      "get": {
        "operationId": "get_api_backup_destinations_id",
        "tags": [
          "backup"
        ],
        "summary": "Fetch a single backup destination; decrypted cloud-credential env vars are only included for callers who can manage backups, and the password is always stripped",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Backup destination id (from GET /api/backup/destinations)"
          }
        ],
        "responses": {
          "200": {
            "description": "The backup destination object (envVars included only for \"backups:manage\" callers; password always stripped)"
          },
          "400": {
            "description": "Invalid id (not a number)"
          },
          "404": {
            "description": "Destination not found"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Permission denial (403, \"backups:view\") is produced by the shared requireBackups route guard."
      },
      "put": {
        "operationId": "put_api_backup_destinations_id",
        "tags": [
          "backup"
        ],
        "summary": "Update a backup destination, re-validating repository and flags when supplied and re-registering maintenance schedules when policies change",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Backup destination id (from GET /api/backup/destinations)"
          }
        ],
        "responses": {
          "200": {
            "description": "The updated backup destination object (password stripped, envVars echoed back to the managing caller)"
          },
          "400": {
            "description": "Invalid input — invalid id, unsupported/SSRF-blocked repository, invalid restic flags, invalid policy cron, or switching to a local repository used by a remote-environment config"
          },
          "404": {
            "description": "Destination not found"
          },
          "409": {
            "description": "A backup using this destination is currently running, or a destination with the new name already exists"
          },
          "500": {
            "description": "Update failed (persistence error)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Permission denial (403, \"backups:manage\") is produced by the shared requireBackups route guard.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "repository": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  },
                  "envVars": {
                    "type": "object",
                    "properties": {}
                  },
                  "flags": {
                    "type": "string"
                  },
                  "hostPath": {
                    "type": "string"
                  },
                  "policies": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "name": "S3 Offsite (renamed)",
                "policies": "{\"pruneEnabled\":true,\"pruneSchedule\":\"0 0 1 * *\"}"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_backup_destinations_id",
        "tags": [
          "backup"
        ],
        "summary": "Delete a backup destination and unregister all its maintenance and dependent backup-config schedules",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Backup destination id (from GET /api/backup/destinations)"
          }
        ],
        "responses": {
          "200": {
            "description": "Returns { success: true } once the destination is deleted"
          },
          "400": {
            "description": "Invalid id (not a number)"
          },
          "404": {
            "description": "Destination not found"
          },
          "409": {
            "description": "A backup using this destination is currently running — try again once it finishes"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Permission denial (403, \"backups:manage\") is produced by the shared requireBackups route guard."
      }
    },
    "/api/backup/destinations/{id}/init": {
      "post": {
        "operationId": "post_api_backup_destinations_id_init",
        "tags": [
          "backup"
        ],
        "summary": "Initialize the restic repository for a destination and record the resulting test status",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Backup destination id (from GET /api/backup/destinations)"
          }
        ],
        "responses": {
          "200": {
            "description": "Returns { success: true, message } when the repository is initialized"
          },
          "400": {
            "description": "Invalid id (not a number)"
          },
          "404": {
            "description": "Destination not found"
          },
          "500": {
            "description": "Repository initialization failed; returns { success: false, error }"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Permission denial (403, \"backups:manage\") is produced by the shared requireBackups route guard."
      }
    },
    "/api/backup/destinations/{id}/rotate-key": {
      "post": {
        "operationId": "post_api_backup_destinations_id_rotate-key",
        "tags": [
          "backup"
        ],
        "summary": "Rotate the restic repository password for a destination and persist the new password",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Backup destination id (from GET /api/backup/destinations)"
          }
        ],
        "responses": {
          "200": {
            "description": "Returns { success: true } once the password is rotated and the database is updated"
          },
          "400": {
            "description": "Invalid input — invalid id, missing passwords, or the current password is incorrect"
          },
          "404": {
            "description": "Destination not found"
          },
          "409": {
            "description": "restic rotated the key but the database write failed (manual recovery needed; the response includes dbOutOfSync:true)"
          },
          "500": {
            "description": "restic call failed for an unrelated reason"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Permission denial (403, \"backups:manage\") is produced by the shared requireBackups route guard.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "currentPassword": {
                    "type": "string"
                  },
                  "newPassword": {
                    "type": "string"
                  }
                },
                "required": [
                  "currentPassword",
                  "newPassword"
                ]
              },
              "example": {
                "currentPassword": "***",
                "newPassword": "***"
              }
            }
          }
        }
      }
    },
    "/api/backup/destinations/{id}/task": {
      "post": {
        "operationId": "post_api_backup_destinations_id_task",
        "tags": [
          "backup"
        ],
        "summary": "Run a restic repository maintenance task (unlock, check, prune, stats, repair-index or repair-snapshots) against a destination",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Backup destination id (from GET /api/backup/destinations)"
          }
        ],
        "responses": {
          "200": {
            "description": "The task result object — { success: true, ... } on success, or { success: false, error } when the task itself fails"
          },
          "400": {
            "description": "Invalid input — invalid destination id, or an unknown task name (must be one of unlock, check, prune, stats, repair-index, repair-snapshots)"
          },
          "403": {
            "description": "No access to an environment whose configs use this destination (enterprise)"
          },
          "404": {
            "description": "Destination not found"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Permission denial (403, \"backups:manage\") is produced by the shared requireBackups route guard. Streamed via createJobResponse — a task failure never surfaces as an HTTP 500; it comes back as a 200 with a failed job result (JSON-preferring callers) or a job/SSE 'result' event with success:false (streaming callers).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "task": {
                    "type": "string"
                  }
                },
                "required": [
                  "task"
                ]
              },
              "example": {
                "task": "check"
              }
            }
          }
        }
      }
    },
    "/api/backup/destinations/{id}/test": {
      "post": {
        "operationId": "post_api_backup_destinations_id_test",
        "tags": [
          "backup"
        ],
        "summary": "Test connectivity to a saved backup destination's repository and update its stored test status",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Backup destination id (from GET /api/backup/destinations)"
          }
        ],
        "responses": {
          "200": {
            "description": "Test result — { success: true, status: \"success\" } when reachable, or { success: false, status: \"needs_init\" | \"failed\", error } otherwise"
          },
          "400": {
            "description": "Invalid id (not a number)"
          },
          "404": {
            "description": "Destination not found"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Permission denial (403, \"backups:manage\") is produced by the shared requireBackups route guard."
      }
    },
    "/api/backup/destinations/{id}/verify": {
      "post": {
        "operationId": "post_api_backup_destinations_id_verify",
        "tags": [
          "backup"
        ],
        "summary": "Verify the integrity of a destination's backups by reading a data subset, streaming progress as a Server-Sent Events job",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Backup destination id (from GET /api/backup/destinations)"
          }
        ],
        "responses": {
          "200": {
            "description": "Server-Sent Events stream of progress and a final result event ({ success, ... })"
          },
          "400": {
            "description": "Invalid destination id (not a number)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Returns a text/event-stream that emits `progress` events and a final `result` event. Permission denial (403, \"backups:manage\") is produced by the shared requireBackups route guard.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "dataSubset": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "dataSubset": "5%"
              }
            }
          }
        }
      }
    },
    "/api/backup/destinations/test": {
      "post": {
        "operationId": "post_api_backup_destinations_test",
        "tags": [
          "backup"
        ],
        "summary": "Test a backup repository — either a saved destination (by destinationId) or inline credentials supplied for a not-yet-saved destination",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Test result — { success: true } when the repository is reachable, or { success: false, needsInit?, error } otherwise"
          },
          "400": {
            "description": "Invalid input — missing repository/password for an inline test, or an unsupported/SSRF-blocked repository"
          },
          "404": {
            "description": "Destination not found (when testing a saved destinationId)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Provide `destinationId` to test a saved destination using its stored credentials, or omit it and supply `repository`/`password`/`envVars` to test before saving. Permission denial (403, \"backups:manage\") is produced by the shared requireBackups route guard. destinationId from GET /api/backup/destinations.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "destinationId": {
                    "type": "integer"
                  },
                  "repository": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  },
                  "envVars": {
                    "type": "object",
                    "properties": {}
                  }
                }
              },
              "example": {
                "repository": "s3:s3.amazonaws.com/my-bucket/restic",
                "password": "***",
                "envVars": {
                  "AWS_ACCESS_KEY_ID": "***",
                  "AWS_SECRET_ACCESS_KEY": "***"
                }
              }
            }
          }
        }
      }
    },
    "/api/backup/restore": {
      "post": {
        "operationId": "post_api_backup_restore",
        "tags": [
          "backup"
        ],
        "summary": "Restore a backup snapshot in-place or to a new location, streaming progress as a Server-Sent Events job",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Server-Sent Events stream of progress and a final result event (status \"success\" or \"error\")"
          },
          "400": {
            "description": "Invalid restore request; the response \"issues\" array lists the validation problems"
          },
          "403": {
            "description": "Permission denied — requires \"backups:manage\", or no access to the target or snapshot-owning environment"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Returns a text/event-stream that emits `progress` events and a final `result` event. An in-place restore is destructive and requires confirmOverwrite:true. Authorization and snapshot-environment access are checked before the request shape is validated. destinationId from GET /api/backup/destinations. snapshotId from GET /api/backup/snapshots. environmentId from GET /api/environments.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "destinationId": {
                    "type": "integer"
                  },
                  "snapshotId": {
                    "type": "string"
                  },
                  "mode": {
                    "type": "string"
                  },
                  "targetType": {
                    "type": "string"
                  },
                  "volumes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "environmentId": {
                    "type": "integer"
                  },
                  "confirmOverwrite": {
                    "type": "boolean"
                  },
                  "targetPath": {
                    "type": "string"
                  },
                  "targetName": {
                    "type": "string"
                  },
                  "postRestore": {
                    "type": "string"
                  },
                  "volumeDestinations": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {}
                    }
                  }
                },
                "required": [
                  "destinationId",
                  "snapshotId"
                ]
              },
              "example": {
                "destinationId": 3,
                "snapshotId": "a1b2c3d4",
                "mode": "new-location",
                "targetType": "container",
                "targetName": "nextcloud-restored",
                "environmentId": 1,
                "confirmOverwrite": false
              }
            }
          }
        }
      }
    },
    "/api/backup/restore/preview": {
      "post": {
        "operationId": "post_api_backup_restore_preview",
        "tags": [
          "backup"
        ],
        "summary": "Preview the contents of a snapshot (volumes, metadata) before running a restore",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The snapshot preview object (contents/metadata used to plan a restore)"
          },
          "400": {
            "description": "Invalid input — missing destinationId/snapshotId or an invalid snapshot id"
          },
          "403": {
            "description": "Permission denied — requires \"backups:manage\", or no access to the target or snapshot-owning environment"
          },
          "500": {
            "description": "Failed to build the preview (restic error)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "destinationId from GET /api/backup/destinations. snapshotId from GET /api/backup/snapshots. environmentId from GET /api/environments.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "destinationId": {
                    "type": "integer"
                  },
                  "snapshotId": {
                    "type": "string"
                  },
                  "environmentId": {
                    "type": "integer"
                  }
                },
                "required": [
                  "destinationId",
                  "snapshotId"
                ]
              },
              "example": {
                "destinationId": 3,
                "snapshotId": "a1b2c3d4"
              }
            }
          }
        }
      }
    },
    "/api/backup/restore/stop": {
      "post": {
        "operationId": "post_api_backup_restore_stop",
        "tags": [
          "backup"
        ],
        "summary": "Cancel a running restore — kills the restore helper for a given snapshotId, or all restore helpers when snapshotId is omitted",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Returns { success: true, stopped } where \"stopped\" indicates whether a running restore helper was actually killed"
          },
          "400": {
            "description": "Invalid snapshotId"
          },
          "403": {
            "description": "Permission denied — requires \"backups:manage\", or no access to the targeted environment"
          },
          "500": {
            "description": "Failed to cancel the restore (internal error)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "snapshotId from GET /api/backup/snapshots. environmentId from GET /api/environments.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "snapshotId": {
                    "type": "string"
                  },
                  "environmentId": {
                    "type": "integer"
                  }
                }
              },
              "example": {
                "snapshotId": "a1b2c3d4"
              }
            }
          }
        }
      }
    },
    "/api/backup/snapshots": {
      "get": {
        "operationId": "get_api_backup_snapshots",
        "tags": [
          "backup"
        ],
        "summary": "List backup snapshots, either for a specific backup config or across a destination; exactly one of configId or destinationId is required",
        "parameters": [
          {
            "name": "configId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "List snapshots for this backup configuration (matched by its stable config-id tag) (from GET /api/backup/configs)"
          },
          {
            "name": "destinationId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "List all snapshots in this destination (mutually exclusive with configId) (from GET /api/backup/destinations)"
          },
          {
            "name": "allDestinations",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "When \"true\" (with configId), search this config's snapshots across every destination"
          }
        ],
        "responses": {
          "200": {
            "description": "Array of snapshot objects"
          },
          "400": {
            "description": "Neither configId nor destinationId supplied, or an invalid configId/destinationId"
          },
          "403": {
            "description": "Permission denied — requires \"backups:view\", or no access to the config's environment"
          },
          "404": {
            "description": "Backup configuration not found (when configId is supplied)"
          },
          "500": {
            "description": "Failed to list snapshots (restic error)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "When allDestinations=true (with configId) all destinations are searched and a partial result is signalled via the X-Incomplete-Destinations response header. Enterprise callers only see snapshots for environments they can access."
      }
    },
    "/api/backup/snapshots/{id}": {
      "delete": {
        "operationId": "delete_api_backup_snapshots_id",
        "tags": [
          "backup"
        ],
        "summary": "Forget and prune a single snapshot from a destination (destructive restic forget --prune), with a server-authoritative environment access gate",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Restic snapshot id to forget (from GET /api/backup/snapshots)"
          },
          {
            "name": "destinationId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Destination holding the snapshot (required) (from GET /api/backup/destinations)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Optional environment id for an early enterprise access check; the authoritative gate resolves the snapshot's owning environment server-side (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Returns { success: true } once the snapshot is forgotten and pruned"
          },
          "400": {
            "description": "Missing/invalid destinationId or an invalid snapshot id"
          },
          "403": {
            "description": "Permission denied — requires \"backups:manage\", or no access to the snapshot's owning environment"
          },
          "404": {
            "description": "Snapshot not found for this installation (not owned)"
          },
          "500": {
            "description": "Failed to forget the snapshot (restic error)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/backup/snapshots/{id}/browse": {
      "get": {
        "operationId": "get_api_backup_snapshots_id_browse",
        "tags": [
          "backup"
        ],
        "summary": "Browse the files and directories at a path inside a snapshot, with a server-authoritative environment access gate",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Restic snapshot id to browse (from GET /api/backup/snapshots)"
          },
          {
            "name": "destinationId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Destination holding the snapshot (required) (from GET /api/backup/destinations)"
          },
          {
            "name": "path",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Directory path to list inside the snapshot (defaults to \"/\")"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Optional environment id for an early enterprise access check; the authoritative gate resolves the snapshot's owning environment server-side (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Returns { entries, path } — the directory entries at the requested path"
          },
          "400": {
            "description": "Missing/invalid destinationId or an invalid snapshot id"
          },
          "403": {
            "description": "Permission denied — requires \"backups:view\", or no access to the snapshot's owning environment"
          },
          "500": {
            "description": "Failed to browse the snapshot (restic error)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/backup/snapshots/{id}/dump": {
      "get": {
        "operationId": "get_api_backup_snapshots_id_dump",
        "tags": [
          "backup"
        ],
        "summary": "Dump a file or directory from a snapshot — inline JSON preview for text files, or a raw binary download (octet-stream for files, tar for directories) when download=1",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Restic snapshot id (from GET /api/backup/snapshots)"
          },
          {
            "name": "destinationId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Destination holding the snapshot (required) (from GET /api/backup/destinations)"
          },
          {
            "name": "path",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Path inside the snapshot to read (must be under /volumes or /metadata)"
          },
          {
            "name": "download",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Set to \"1\" to download raw bytes instead of an inline preview"
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Set to \"directory\" to download a directory as a tar archive (with download=1)"
          }
        ],
        "responses": {
          "200": {
            "description": "Inline preview returns { content } (JSON); with download=1 the body is the raw file bytes (application/octet-stream) or a tar archive (application/x-tar)"
          },
          "400": {
            "description": "Missing/invalid destinationId, missing path, an invalid snapshot id, or a path outside the allowed roots / containing traversal"
          },
          "403": {
            "description": "metadata.json cannot be downloaded raw (download=1); use this endpoint without download for the redacted inline preview"
          },
          "404": {
            "description": "The redacted metadata.json preview could not be parsed"
          },
          "500": {
            "description": "Failed to read the file/directory from the snapshot (restic error)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Paths are restricted to the /volumes and /metadata snapshot roots and rejected if they contain traversal (\"..\"). Permission (\"backups:view\") and environment-access denials are produced by the shared requireBackups/guardSnapshotEnvAccess route guards."
      }
    },
    "/api/backup/snapshots/{id}/metadata": {
      "get": {
        "operationId": "get_api_backup_snapshots_id_metadata",
        "tags": [
          "backup"
        ],
        "summary": "Fetch the stored metadata for a snapshot, with a server-authoritative environment access gate",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Restic snapshot id (from GET /api/backup/snapshots)"
          },
          {
            "name": "destinationId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Destination holding the snapshot (required) (from GET /api/backup/destinations)"
          }
        ],
        "responses": {
          "200": {
            "description": "The snapshot metadata object"
          },
          "400": {
            "description": "Missing/invalid destinationId or an invalid snapshot id"
          },
          "404": {
            "description": "No metadata available for this snapshot"
          },
          "500": {
            "description": "Failed to read the snapshot metadata (restic error)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Permission (\"backups:view\") and environment-access denials (403) are produced by the shared requireBackups/guardSnapshotEnvAccess route guards; the owning environment is resolved server-side from the snapshot's own tag, not a caller-supplied param."
      }
    },
    "/api/backup/snapshots/diff": {
      "get": {
        "operationId": "get_api_backup_snapshots_diff",
        "tags": [
          "backup"
        ],
        "summary": "Compute the file-level difference between two snapshots in the same destination, gating access on both snapshots' owning environments",
        "parameters": [
          {
            "name": "destinationId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Destination holding both snapshots (required) (from GET /api/backup/destinations)"
          },
          {
            "name": "snapshotA",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "First (base) restic snapshot id (required)"
          },
          {
            "name": "snapshotB",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Second (compared) restic snapshot id (required)"
          }
        ],
        "responses": {
          "200": {
            "description": "The diff result object (added/removed/changed entries between the two snapshots)"
          },
          "400": {
            "description": "Missing required params (destinationId, snapshotA, snapshotB) or an invalid snapshot id"
          },
          "500": {
            "description": "Failed to diff the snapshots (restic error)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Permission (\"backups:view\") and environment-access denials (403) are produced by the shared requireBackups/guardSnapshotEnvAccess route guards."
      }
    },
    "/api/backup/stack-dir-listing": {
      "get": {
        "operationId": "get_api_backup_stack-dir-listing",
        "tags": [
          "backup"
        ],
        "summary": "Probe the target host and list the actual contents of a stack's directory, for the backup create/edit dialog's file picker",
        "parameters": [
          {
            "name": "target",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Target stack name"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the stack belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "The stack directory probe result — kind \"listed\"/\"tar\" with hostPath/localStackDir and entries, or kind \"helper-failed\"/\"unknown\" with a reason (never throws for an operational failure)"
          },
          "400": {
            "description": "target is required"
          },
          "403": {
            "description": "Permission denied (requires backups:view), or no access to the target's environment (enterprise)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/backup/stack-path": {
      "get": {
        "operationId": "get_api_backup_stack-path",
        "tags": [
          "backup"
        ],
        "summary": "Preview where a stack's directory will be captured from on the host, without running a backup",
        "parameters": [
          {
            "name": "target",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Target stack name"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the stack belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "The resolved capture path preview — kind \"candidate\" with hostPath, kind \"tar\" with composeFile only, or kind \"unknown\" with a reason"
          },
          "400": {
            "description": "target is required"
          },
          "403": {
            "description": "Permission denied (requires backups:view), or no access to the target's environment (enterprise)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/batch": {
      "post": {
        "operationId": "post_api_batch",
        "tags": [
          "batch"
        ],
        "summary": "Run a bulk operation (start/stop/restart/pause/unpause/remove/down, depending on entity type) across many containers, images, volumes, networks, or stacks; clients that send Accept application/json get a synchronous summary, others get a jobId and progress over Server-Sent Events",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the entities belong to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Either a synchronous completion summary ({type:\"complete\", summary:{total, success, failed}}) or an async job handle ({jobId}) whose progress streams over SSE"
          },
          "400": {
            "description": "Invalid JSON body, invalid entity type, invalid operation for the entity type, or empty items array"
          },
          "403": {
            "description": "Permission denied for the requested operation, or environment access denied on enterprise"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "operation": {
                    "type": "string"
                  },
                  "entityType": {
                    "type": "string"
                  },
                  "items": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "id",
                        "name"
                      ]
                    }
                  },
                  "options": {
                    "type": "object",
                    "properties": {
                      "force": {
                        "type": "boolean"
                      },
                      "removeVolumes": {
                        "type": "boolean"
                      }
                    }
                  }
                },
                "required": [
                  "operation",
                  "entityType",
                  "items"
                ]
              },
              "example": {
                "operation": "restart",
                "entityType": "containers",
                "items": [
                  {
                    "id": "abc123",
                    "name": "web"
                  }
                ],
                "options": {
                  "force": false
                }
              }
            }
          }
        }
      }
    },
    "/api/changelog": {
      "get": {
        "operationId": "get_api_changelog",
        "tags": [
          "changelog"
        ],
        "summary": "Return the bundled Dockhand changelog data",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "version": {
                        "type": "string"
                      },
                      "date": {
                        "type": "string"
                      },
                      "changes": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    },
                    "required": [
                      "version"
                    ]
                  }
                },
                "example": [
                  {
                    "version": "1.0.39",
                    "date": "2026-06-01",
                    "changes": [
                      "Fixed image export streaming"
                    ]
                  }
                ]
              }
            }
          }
        },
        "security": []
      }
    },
    "/api/config-sets": {
      "get": {
        "operationId": "get_api_config-sets",
        "tags": [
          "config-sets"
        ],
        "summary": "List all config sets (reusable bundles of env vars, labels, ports, volumes, and runtime defaults)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      },
                      "name": {
                        "type": "string"
                      },
                      "description": {
                        "type": "string"
                      },
                      "envVars": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "key": {
                              "type": "string"
                            },
                            "value": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "key",
                            "value"
                          ]
                        }
                      },
                      "labels": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "key": {
                              "type": "string"
                            },
                            "value": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "key",
                            "value"
                          ]
                        }
                      },
                      "ports": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "hostPort": {
                              "type": "string"
                            },
                            "containerPort": {
                              "type": "string"
                            },
                            "protocol": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "hostPort",
                            "containerPort",
                            "protocol"
                          ]
                        }
                      },
                      "volumes": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "hostPath": {
                              "type": "string"
                            },
                            "containerPath": {
                              "type": "string"
                            },
                            "mode": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "hostPath",
                            "containerPath",
                            "mode"
                          ]
                        }
                      },
                      "networkMode": {
                        "type": "string"
                      },
                      "restartPolicy": {
                        "type": "string"
                      },
                      "createdAt": {
                        "type": "string"
                      },
                      "updatedAt": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "networkMode",
                      "restartPolicy",
                      "createdAt",
                      "updatedAt"
                    ]
                  }
                },
                "example": [
                  {
                    "id": 1,
                    "name": "web-defaults",
                    "description": "Defaults for web stacks",
                    "envVars": [
                      {
                        "key": "TZ",
                        "value": "UTC"
                      }
                    ],
                    "labels": [],
                    "ports": [],
                    "volumes": [],
                    "networkMode": "bridge",
                    "restartPolicy": "unless-stopped",
                    "createdAt": "2026-06-01T10:00:00Z",
                    "updatedAt": "2026-06-01T10:00:00Z"
                  }
                ]
              }
            }
          },
          "403": {
            "description": "Permission denied (requires configsets:view)"
          },
          "500": {
            "description": "Failed to fetch config sets"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_config-sets",
        "tags": [
          "config-sets"
        ],
        "summary": "Create a new config set",
        "parameters": [],
        "responses": {
          "201": {
            "description": "Config set created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    },
                    "envVars": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string"
                          },
                          "value": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "key",
                          "value"
                        ]
                      }
                    },
                    "labels": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string"
                          },
                          "value": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "key",
                          "value"
                        ]
                      }
                    },
                    "ports": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "hostPort": {
                            "type": "string"
                          },
                          "containerPort": {
                            "type": "string"
                          },
                          "protocol": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "hostPort",
                          "containerPort",
                          "protocol"
                        ]
                      }
                    },
                    "volumes": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "hostPath": {
                            "type": "string"
                          },
                          "containerPath": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "hostPath",
                          "containerPath",
                          "mode"
                        ]
                      }
                    },
                    "networkMode": {
                      "type": "string"
                    },
                    "restartPolicy": {
                      "type": "string"
                    },
                    "createdAt": {
                      "type": "string"
                    },
                    "updatedAt": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "networkMode",
                    "restartPolicy",
                    "createdAt",
                    "updatedAt"
                  ]
                },
                "example": {
                  "id": 1,
                  "name": "web-defaults",
                  "description": "Defaults for web stacks",
                  "envVars": [
                    {
                      "key": "TZ",
                      "value": "UTC"
                    }
                  ],
                  "labels": [],
                  "ports": [],
                  "volumes": [],
                  "networkMode": "bridge",
                  "restartPolicy": "unless-stopped",
                  "createdAt": "2026-06-01T10:00:00Z",
                  "updatedAt": "2026-06-01T10:00:00Z"
                }
              }
            }
          },
          "400": {
            "description": "Name is required, or a config set with this name already exists"
          },
          "403": {
            "description": "Permission denied (requires configsets:create)"
          },
          "500": {
            "description": "Failed to create config set"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "envVars": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "key": {
                          "type": "string"
                        },
                        "value": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "key",
                        "value"
                      ]
                    }
                  },
                  "labels": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "key": {
                          "type": "string"
                        },
                        "value": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "key",
                        "value"
                      ]
                    }
                  },
                  "ports": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "hostPort": {
                          "type": "string"
                        },
                        "containerPort": {
                          "type": "string"
                        },
                        "protocol": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "hostPort",
                        "containerPort",
                        "protocol"
                      ]
                    }
                  },
                  "volumes": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "hostPath": {
                          "type": "string"
                        },
                        "containerPath": {
                          "type": "string"
                        },
                        "mode": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "hostPath",
                        "containerPath",
                        "mode"
                      ]
                    }
                  },
                  "networkMode": {
                    "type": "string"
                  },
                  "restartPolicy": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              },
              "example": {
                "name": "web-defaults",
                "description": "Defaults for web stacks",
                "envVars": [
                  {
                    "key": "TZ",
                    "value": "UTC"
                  }
                ],
                "networkMode": "bridge",
                "restartPolicy": "unless-stopped"
              }
            }
          }
        }
      }
    },
    "/api/config-sets/{id}": {
      "get": {
        "operationId": "get_api_config-sets_id",
        "tags": [
          "config-sets"
        ],
        "summary": "Fetch a single config set by its numeric ID",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Config set ID (from GET /api/config-sets)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    },
                    "envVars": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string"
                          },
                          "value": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "key",
                          "value"
                        ]
                      }
                    },
                    "labels": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string"
                          },
                          "value": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "key",
                          "value"
                        ]
                      }
                    },
                    "ports": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "hostPort": {
                            "type": "string"
                          },
                          "containerPort": {
                            "type": "string"
                          },
                          "protocol": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "hostPort",
                          "containerPort",
                          "protocol"
                        ]
                      }
                    },
                    "volumes": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "hostPath": {
                            "type": "string"
                          },
                          "containerPath": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "hostPath",
                          "containerPath",
                          "mode"
                        ]
                      }
                    },
                    "networkMode": {
                      "type": "string"
                    },
                    "restartPolicy": {
                      "type": "string"
                    },
                    "createdAt": {
                      "type": "string"
                    },
                    "updatedAt": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "networkMode",
                    "restartPolicy",
                    "createdAt",
                    "updatedAt"
                  ]
                },
                "example": {
                  "id": 1,
                  "name": "web-defaults",
                  "description": "Defaults for web stacks",
                  "envVars": [
                    {
                      "key": "TZ",
                      "value": "UTC"
                    }
                  ],
                  "labels": [],
                  "ports": [],
                  "volumes": [],
                  "networkMode": "bridge",
                  "restartPolicy": "unless-stopped",
                  "createdAt": "2026-06-01T10:00:00Z",
                  "updatedAt": "2026-06-01T10:00:00Z"
                }
              }
            }
          },
          "400": {
            "description": "Invalid ID"
          },
          "403": {
            "description": "Permission denied (requires configsets:view)"
          },
          "404": {
            "description": "Config set not found"
          },
          "500": {
            "description": "Failed to fetch config set"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "operationId": "put_api_config-sets_id",
        "tags": [
          "config-sets"
        ],
        "summary": "Update an existing config set by ID",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Config set ID (from GET /api/config-sets)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    },
                    "envVars": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string"
                          },
                          "value": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "key",
                          "value"
                        ]
                      }
                    },
                    "labels": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string"
                          },
                          "value": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "key",
                          "value"
                        ]
                      }
                    },
                    "ports": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "hostPort": {
                            "type": "string"
                          },
                          "containerPort": {
                            "type": "string"
                          },
                          "protocol": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "hostPort",
                          "containerPort",
                          "protocol"
                        ]
                      }
                    },
                    "volumes": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "hostPath": {
                            "type": "string"
                          },
                          "containerPath": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "hostPath",
                          "containerPath",
                          "mode"
                        ]
                      }
                    },
                    "networkMode": {
                      "type": "string"
                    },
                    "restartPolicy": {
                      "type": "string"
                    },
                    "createdAt": {
                      "type": "string"
                    },
                    "updatedAt": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "networkMode",
                    "restartPolicy",
                    "createdAt",
                    "updatedAt"
                  ]
                },
                "example": {
                  "id": 1,
                  "name": "web-defaults",
                  "description": "Defaults for web stacks",
                  "envVars": [],
                  "labels": [],
                  "ports": [],
                  "volumes": [],
                  "networkMode": "host",
                  "restartPolicy": "always",
                  "createdAt": "2026-06-01T10:00:00Z",
                  "updatedAt": "2026-06-02T12:00:00Z"
                }
              }
            }
          },
          "400": {
            "description": "Invalid ID, or a config set with this name already exists"
          },
          "403": {
            "description": "Permission denied (requires configsets:edit)"
          },
          "404": {
            "description": "Config set not found"
          },
          "500": {
            "description": "Failed to update config set"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "envVars": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "key": {
                          "type": "string"
                        },
                        "value": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "key",
                        "value"
                      ]
                    }
                  },
                  "labels": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "key": {
                          "type": "string"
                        },
                        "value": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "key",
                        "value"
                      ]
                    }
                  },
                  "ports": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "hostPort": {
                          "type": "string"
                        },
                        "containerPort": {
                          "type": "string"
                        },
                        "protocol": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "hostPort",
                        "containerPort",
                        "protocol"
                      ]
                    }
                  },
                  "volumes": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "hostPath": {
                          "type": "string"
                        },
                        "containerPath": {
                          "type": "string"
                        },
                        "mode": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "hostPath",
                        "containerPath",
                        "mode"
                      ]
                    }
                  },
                  "networkMode": {
                    "type": "string"
                  },
                  "restartPolicy": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "name": "web-defaults",
                "networkMode": "host",
                "restartPolicy": "always"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_config-sets_id",
        "tags": [
          "config-sets"
        ],
        "summary": "Delete a config set by ID",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Config set ID (from GET /api/config-sets)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid ID"
          },
          "403": {
            "description": "Permission denied (requires configsets:delete)"
          },
          "404": {
            "description": "Config set not found"
          },
          "500": {
            "description": "Failed to delete config set"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/containers": {
      "get": {
        "operationId": "get_api_containers",
        "tags": [
          "containers"
        ],
        "summary": "List containers for one environment (dockhand.hidden=true label is filtered out)",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id — an empty array is returned if omitted (from GET /api/environments)"
          },
          {
            "name": "all",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Include stopped containers (default true; pass \"false\" to only show running ones)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string"
                      },
                      "name": {
                        "type": "string"
                      },
                      "image": {
                        "type": "string"
                      },
                      "state": {
                        "type": "string"
                      },
                      "status": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "image",
                      "state",
                      "status"
                    ]
                  }
                },
                "example": [
                  {
                    "id": "a1b2c3d4e5f6",
                    "name": "immich_server",
                    "image": "ghcr.io/immich-app/immich-server:latest",
                    "state": "running",
                    "status": "Up 2 hours (healthy)"
                  }
                ]
              }
            }
          },
          "403": {
            "description": "Permission denied, or access denied to this environment"
          },
          "404": {
            "description": "Environment not found"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_containers",
        "tags": [
          "containers"
        ],
        "summary": "Create a new container in an environment (auto-pulls the image first if it isn't present locally), optionally starting it right away",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "id": {
                      "type": "string"
                    },
                    "imagePulled": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success",
                    "id"
                  ]
                },
                "example": {
                  "success": true,
                  "id": "string",
                  "imagePulled": true
                }
              }
            }
          },
          "403": {
            "description": "Permission denied, or access denied to this environment"
          },
          "500": {
            "description": "Container creation failed, or the image could not be pulled"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "image": {
                    "type": "string"
                  },
                  "ports": {
                    "type": "object",
                    "properties": {}
                  },
                  "volumes": {
                    "type": "object",
                    "properties": {}
                  },
                  "volumeBinds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "env": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "labels": {
                    "type": "object",
                    "properties": {}
                  },
                  "cmd": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "entrypoint": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "workingDir": {
                    "type": "string"
                  },
                  "restartPolicy": {
                    "type": "string"
                  },
                  "restartMaxRetries": {
                    "type": "integer"
                  },
                  "networkMode": {
                    "type": "string"
                  },
                  "additionalNetworks": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "networkAliases": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "networkIpv4Address": {
                    "type": "string"
                  },
                  "networkIpv6Address": {
                    "type": "string"
                  },
                  "startAfterCreate": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "name",
                  "image"
                ]
              },
              "example": {
                "name": "my-app",
                "image": "nginx:latest",
                "startAfterCreate": true
              }
            }
          }
        }
      }
    },
    "/api/containers/{id}": {
      "get": {
        "operationId": "get_api_containers_id",
        "tags": [
          "containers"
        ],
        "summary": "Return the full Docker inspect payload for a single container",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "The raw Docker inspect object for the container"
          },
          "403": {
            "description": "Permission denied, or (enterprise) no access to the requested environment"
          },
          "404": {
            "description": "Container not found"
          },
          "500": {
            "description": "Failed to inspect the container (e.g. it no longer exists or the daemon is unreachable)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "delete": {
        "operationId": "delete_api_containers_id",
        "tags": [
          "containers"
        ],
        "summary": "Remove a container and clean up its auto-update schedule and pending-update record",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          },
          {
            "name": "force",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Force removal of a running container (Docker force flag)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "403": {
            "description": "Permission denied, or (enterprise) no access to the requested environment"
          },
          "404": {
            "description": "Container not found"
          },
          "500": {
            "description": "Failed to remove the container"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/containers/{id}/exec": {
      "post": {
        "operationId": "post_api_containers_id_exec",
        "tags": [
          "containers"
        ],
        "summary": "Create a Docker exec instance in a container and return its ID plus the Docker connection info for a terminal WebSocket",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "envId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "execId": {
                      "type": "string"
                    },
                    "connectionInfo": {
                      "type": "object",
                      "properties": {
                        "type": {
                          "type": "string"
                        },
                        "host": {
                          "type": "string"
                        },
                        "port": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "type"
                      ]
                    }
                  },
                  "required": [
                    "execId"
                  ]
                },
                "example": {
                  "execId": "string",
                  "connectionInfo": {
                    "type": "string",
                    "host": "string",
                    "port": 0
                  }
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated"
          },
          "403": {
            "description": "Permission denied"
          },
          "500": {
            "description": "Failed to create the exec instance"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "shell": {
                    "type": "string"
                  },
                  "user": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "shell": "/bin/sh",
                "user": "root"
              }
            }
          }
        }
      }
    },
    "/api/containers/{id}/files": {
      "get": {
        "operationId": "get_api_containers_id_files",
        "tags": [
          "containers"
        ],
        "summary": "List the contents of a directory inside a container's filesystem",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          },
          {
            "name": "path",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Absolute directory path inside the container (default \"/\")"
          },
          {
            "name": "simpleLs",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Use a lightweight `ls` listing instead of a full stat of each entry"
          }
        ],
        "responses": {
          "200": {
            "description": "Directory listing (entries with name, type, size and permission metadata)"
          },
          "403": {
            "description": "Permission denied"
          },
          "404": {
            "description": "Container not found"
          },
          "500": {
            "description": "Failed to list the directory"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/containers/{id}/files/chmod": {
      "post": {
        "operationId": "post_api_containers_id_files_chmod",
        "tags": [
          "containers"
        ],
        "summary": "Change the mode (permissions) of a file or directory inside a container (requires the 'exec' permission)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "path": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "recursive": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success",
                    "path",
                    "mode",
                    "recursive"
                  ]
                },
                "example": {
                  "success": true,
                  "path": "/app/entrypoint.sh",
                  "mode": "755",
                  "recursive": false
                }
              }
            }
          },
          "400": {
            "description": "Path or mode missing, an invalid chmod mode, or the container is not running"
          },
          "403": {
            "description": "Permission denied, read-only file system, or operation not permitted"
          },
          "404": {
            "description": "Path not found"
          },
          "500": {
            "description": "Failed to change permissions"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "path": {
                    "type": "string"
                  },
                  "mode": {
                    "type": "string"
                  },
                  "recursive": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "path",
                  "mode"
                ]
              },
              "example": {
                "path": "/app/entrypoint.sh",
                "mode": "755",
                "recursive": false
              }
            }
          }
        }
      }
    },
    "/api/containers/{id}/files/content": {
      "get": {
        "operationId": "get_api_containers_id_files_content",
        "tags": [
          "containers"
        ],
        "summary": "Read the content of a single file inside a container (max 1 MB)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          },
          {
            "name": "path",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Absolute file path inside the container"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "content": {
                      "type": "string"
                    },
                    "path": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "content",
                    "path"
                  ]
                },
                "example": {
                  "content": "string",
                  "path": "string"
                }
              }
            }
          },
          "400": {
            "description": "Path is missing, the target is a directory, or the container is not running"
          },
          "403": {
            "description": "Permission denied to read the file"
          },
          "404": {
            "description": "File not found"
          },
          "413": {
            "description": "File is larger than the 1 MB read limit"
          },
          "500": {
            "description": "Failed to read the file"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "operationId": "put_api_containers_id_files_content",
        "tags": [
          "containers"
        ],
        "summary": "Overwrite the content of a file inside a container (max 1 MB, requires the 'exec' permission)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          },
          {
            "name": "path",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Absolute file path inside the container"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "path": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success",
                    "path"
                  ]
                },
                "example": {
                  "success": true,
                  "path": "/etc/nginx/nginx.conf"
                }
              }
            }
          },
          "400": {
            "description": "Path is missing, content is missing/not a string, or the container is not running"
          },
          "403": {
            "description": "Permission denied, or the target file system is read-only"
          },
          "404": {
            "description": "Target directory not found"
          },
          "413": {
            "description": "Content is larger than the 1 MB write limit"
          },
          "500": {
            "description": "Failed to write the file"
          },
          "507": {
            "description": "No space left on device"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string"
                  }
                },
                "required": [
                  "content"
                ]
              },
              "example": {
                "content": "server {\n  listen 80;\n}\n"
              }
            }
          }
        }
      }
    },
    "/api/containers/{id}/files/create": {
      "post": {
        "operationId": "post_api_containers_id_files_create",
        "tags": [
          "containers"
        ],
        "summary": "Create an empty file or a directory inside a container (requires the 'exec' permission)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "path": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success",
                    "path",
                    "type"
                  ]
                },
                "example": {
                  "success": true,
                  "path": "/app/data",
                  "type": "directory"
                }
              }
            }
          },
          "400": {
            "description": "Path missing, type not \"file\" or \"directory\", or the container is not running"
          },
          "403": {
            "description": "Permission denied"
          },
          "404": {
            "description": "Parent directory not found"
          },
          "409": {
            "description": "Path already exists"
          },
          "500": {
            "description": "Failed to create the path"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "path": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string"
                  }
                },
                "required": [
                  "path",
                  "type"
                ]
              },
              "example": {
                "path": "/app/data",
                "type": "directory"
              }
            }
          }
        }
      }
    },
    "/api/containers/{id}/files/delete": {
      "delete": {
        "operationId": "delete_api_containers_id_files_delete",
        "tags": [
          "containers"
        ],
        "summary": "Delete a file or directory inside a container (requires the 'exec' permission)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          },
          {
            "name": "path",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Absolute path inside the container to delete"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "path": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success",
                    "path"
                  ]
                },
                "example": {
                  "success": true,
                  "path": "/app/tmp/old.log"
                }
              }
            }
          },
          "400": {
            "description": "Path missing, a refused critical-path delete, a non-empty directory, or the container is not running"
          },
          "403": {
            "description": "Permission denied, or read-only file system"
          },
          "404": {
            "description": "Path not found"
          },
          "500": {
            "description": "Failed to delete the path"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/containers/{id}/files/download": {
      "get": {
        "operationId": "get_api_containers_id_files_download",
        "tags": [
          "containers"
        ],
        "summary": "Download a file or directory from a container as a tar (optionally gzip-compressed) archive attachment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          },
          {
            "name": "path",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Absolute path inside the container to archive and download"
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Archive format, \"tar\" (default) or \"tar.gz\""
          }
        ],
        "responses": {
          "200": {
            "description": "Archive stream (application/x-tar or application/gzip) as a file attachment"
          },
          "400": {
            "description": "Path is missing"
          },
          "403": {
            "description": "Permission denied to access the path"
          },
          "404": {
            "description": "File not found"
          },
          "500": {
            "description": "Failed to download the file"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/containers/{id}/files/rename": {
      "post": {
        "operationId": "post_api_containers_id_files_rename",
        "tags": [
          "containers"
        ],
        "summary": "Rename or move a file or directory inside a container (requires the 'exec' permission)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "oldPath": {
                      "type": "string"
                    },
                    "newPath": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success",
                    "oldPath",
                    "newPath"
                  ]
                },
                "example": {
                  "success": true,
                  "oldPath": "/app/config.yaml",
                  "newPath": "/app/config.old.yaml"
                }
              }
            }
          },
          "400": {
            "description": "oldPath or newPath missing, or the container is not running"
          },
          "403": {
            "description": "Permission denied, or read-only file system"
          },
          "404": {
            "description": "Source path not found"
          },
          "409": {
            "description": "Destination already exists"
          },
          "500": {
            "description": "Failed to rename the path"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "oldPath": {
                    "type": "string"
                  },
                  "newPath": {
                    "type": "string"
                  }
                },
                "required": [
                  "oldPath",
                  "newPath"
                ]
              },
              "example": {
                "oldPath": "/app/config.yaml",
                "newPath": "/app/config.old.yaml"
              }
            }
          }
        }
      }
    },
    "/api/containers/{id}/files/upload": {
      "post": {
        "operationId": "post_api_containers_id_files_upload",
        "tags": [
          "containers"
        ],
        "summary": "Upload one or more files into a container directory (requires the 'exec' permission)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          },
          {
            "name": "path",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Absolute target directory inside the container"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "uploaded": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "success",
                    "uploaded"
                  ]
                },
                "example": {
                  "success": true,
                  "uploaded": [
                    "app.conf"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Target path missing, or no files provided"
          },
          "403": {
            "description": "Permission denied to write to the path"
          },
          "404": {
            "description": "Target directory not found"
          },
          "500": {
            "description": "Failed to upload the files"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Send `multipart/form-data` with one or more `files` parts; each file is tar-wrapped and written into the target directory. Partial success is reported per file in `uploaded`/`errors`."
      }
    },
    "/api/containers/{id}/inspect": {
      "get": {
        "operationId": "get_api_containers_id_inspect",
        "tags": [
          "containers"
        ],
        "summary": "Return the full Docker inspect payload for a container (requires the 'inspect' permission)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "The raw Docker inspect object for the container"
          },
          "403": {
            "description": "Permission denied"
          },
          "500": {
            "description": "Failed to inspect the container"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/containers/{id}/logs": {
      "get": {
        "operationId": "get_api_containers_id_logs",
        "tags": [
          "containers"
        ],
        "summary": "Return the last N lines of a container's combined stdout/stderr logs as a single string",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          },
          {
            "name": "tail",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Number of trailing log lines to return (default 100)"
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only return logs since this time (Unix timestamp or Docker duration, e.g. 10m)"
          },
          {
            "name": "until",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only return logs before this time (Unix timestamp or Docker duration)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "logs": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "logs"
                  ]
                },
                "example": {
                  "logs": "string"
                }
              }
            }
          },
          "403": {
            "description": "Permission denied"
          },
          "500": {
            "description": "Failed to read the container logs"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/containers/{id}/logs/stream": {
      "get": {
        "operationId": "get_api_containers_id_logs_stream",
        "tags": [
          "containers"
        ],
        "summary": "Stream a container's logs live as Server-Sent Events (connected/log/end/error events, with keepalive heartbeats)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          },
          {
            "name": "tail",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Number of trailing log lines to prime the stream with (default \"100\")"
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only stream logs since this time (Unix timestamp or Docker duration, e.g. 10m)"
          },
          {
            "name": "until",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only stream logs before this time (Unix timestamp or Docker duration)"
          }
        ],
        "responses": {
          "200": {
            "description": "Server-Sent Events stream (text/event-stream) of log lines"
          },
          "403": {
            "description": "Permission denied"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Returns a `text/event-stream`. For Hawser Edge environments whose agent is not connected the stream body carries an error event. Docker stdout/stderr frames are demultiplexed server-side; TTY containers stream raw text."
      }
    },
    "/api/containers/{id}/pause": {
      "post": {
        "operationId": "post_api_containers_id_pause",
        "tags": [
          "containers"
        ],
        "summary": "Pause all processes in a running container (requires the container 'stop' permission)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "403": {
            "description": "Permission denied"
          },
          "500": {
            "description": "Failed to pause the container"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/containers/{id}/rename": {
      "post": {
        "operationId": "post_api_containers_id_rename",
        "tags": [
          "containers"
        ],
        "summary": "Rename a container and update any associated auto-update schedule (requires the 'create' permission)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "The new name is missing or not a string"
          },
          "403": {
            "description": "Permission denied"
          },
          "404": {
            "description": "Container not found"
          },
          "500": {
            "description": "Failed to rename the container"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              },
              "example": {
                "name": "my-renamed-container"
              }
            }
          }
        }
      }
    },
    "/api/containers/{id}/restart": {
      "post": {
        "operationId": "post_api_containers_id_restart",
        "tags": [
          "containers"
        ],
        "summary": "Restart a container",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "403": {
            "description": "Permission denied, or (enterprise) no access to the requested environment"
          },
          "404": {
            "description": "Container not found"
          },
          "500": {
            "description": "Failed to restart the container"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/containers/{id}/shells": {
      "get": {
        "operationId": "get_api_containers_id_shells",
        "tags": [
          "containers"
        ],
        "summary": "Probe a container for available shells (bash/sh/zsh/ash) and report the preferred default (requires the 'exec' permission)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "shells": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "defaultShell": {
                      "type": "string"
                    },
                    "allShells": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "path": {
                            "type": "string"
                          },
                          "label": {
                            "type": "string"
                          },
                          "available": {
                            "type": "boolean"
                          }
                        },
                        "required": [
                          "path",
                          "label",
                          "available"
                        ]
                      }
                    }
                  },
                  "required": [
                    "shells",
                    "allShells"
                  ]
                },
                "example": {
                  "shells": [
                    "/bin/sh"
                  ],
                  "defaultShell": "/bin/sh",
                  "allShells": [
                    {
                      "path": "/bin/bash",
                      "label": "Bash",
                      "available": false
                    },
                    {
                      "path": "/bin/sh",
                      "label": "Shell (sh)",
                      "available": true
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Permission denied, or (enterprise) no access to the requested environment"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "On probe failure the endpoint still returns 200 with empty results rather than an error, so callers always get a usable shell list."
      }
    },
    "/api/containers/{id}/start": {
      "post": {
        "operationId": "post_api_containers_id_start",
        "tags": [
          "containers"
        ],
        "summary": "Start a stopped container",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "403": {
            "description": "Permission denied, or (enterprise) no access to the requested environment"
          },
          "404": {
            "description": "Container not found"
          },
          "500": {
            "description": "Failed to start the container"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/containers/{id}/stats": {
      "get": {
        "operationId": "get_api_containers_id_stats",
        "tags": [
          "containers"
        ],
        "summary": "Return a one-shot CPU/memory/network/block-IO stats snapshot for a container (Docker-CLI-equivalent memory accounting)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "cpuPercent": {
                      "type": "number"
                    },
                    "memoryUsage": {
                      "type": "integer"
                    },
                    "memoryRaw": {
                      "type": "integer"
                    },
                    "memoryCache": {
                      "type": "integer"
                    },
                    "memoryLimit": {
                      "type": "integer"
                    },
                    "memoryPercent": {
                      "type": "number"
                    },
                    "networkRx": {
                      "type": "integer"
                    },
                    "networkTx": {
                      "type": "integer"
                    },
                    "blockRead": {
                      "type": "integer"
                    },
                    "blockWrite": {
                      "type": "integer"
                    },
                    "timestamp": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "cpuPercent",
                    "memoryUsage",
                    "memoryRaw",
                    "memoryCache",
                    "memoryLimit",
                    "memoryPercent",
                    "networkRx",
                    "networkTx",
                    "blockRead",
                    "blockWrite",
                    "timestamp"
                  ]
                },
                "example": {
                  "cpuPercent": 0,
                  "memoryUsage": 0,
                  "memoryRaw": 0,
                  "memoryCache": 0,
                  "memoryLimit": 0,
                  "memoryPercent": 0,
                  "networkRx": 0,
                  "networkTx": 0,
                  "blockRead": 0,
                  "blockWrite": 0,
                  "timestamp": 0
                }
              }
            }
          },
          "403": {
            "description": "Permission denied"
          },
          "404": {
            "description": "No environment configured, the environment was not found, or the container was not found"
          },
          "500": {
            "description": "Failed to read the container stats"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/containers/{id}/stop": {
      "post": {
        "operationId": "post_api_containers_id_stop",
        "tags": [
          "containers"
        ],
        "summary": "Stop a running container",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "403": {
            "description": "Permission denied, or (enterprise) no access to the requested environment"
          },
          "404": {
            "description": "Container not found"
          },
          "500": {
            "description": "Failed to stop the container"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/containers/{id}/top": {
      "get": {
        "operationId": "get_api_containers_id_top",
        "tags": [
          "containers"
        ],
        "summary": "List the processes running inside a container (via `ps` in the container, falling back to the Docker top API)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "Titles": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "Processes": {
                      "type": "array",
                      "items": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    },
                    "source": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "Titles",
                    "Processes",
                    "source"
                  ]
                },
                "example": {
                  "Titles": [
                    "string"
                  ],
                  "Processes": [
                    [
                      "string"
                    ]
                  ],
                  "source": "string"
                }
              }
            }
          },
          "403": {
            "description": "Permission denied"
          },
          "500": {
            "description": "Failed to read the container process list"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Uses the container 'inspect' permission. The `source` field indicates whether the result came from an in-container `ps` (`\"ps\"`) or the Docker top API fallback (`\"top\"`)."
      }
    },
    "/api/containers/{id}/unpause": {
      "post": {
        "operationId": "post_api_containers_id_unpause",
        "tags": [
          "containers"
        ],
        "summary": "Resume all processes in a paused container (requires the container 'start' permission)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "403": {
            "description": "Permission denied"
          },
          "500": {
            "description": "Failed to unpause the container"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/containers/{id}/update": {
      "post": {
        "operationId": "post_api_containers_id_update",
        "tags": [
          "containers"
        ],
        "summary": "Recreate a container with updated create-options (optionally re-pulling the image first), preserving its configuration (requires the 'create' permission)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container ID or name (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "id": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success",
                    "id"
                  ]
                },
                "example": {
                  "success": true,
                  "id": "3f4a1c2b9d8e"
                }
              }
            }
          },
          "403": {
            "description": "Permission denied"
          },
          "404": {
            "description": "Container not found"
          },
          "500": {
            "description": "Failed to update the container (e.g. the image pull or recreation failed)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "The body carries the container create-options (image, name, env, ports, volumes, …) alongside the two control flags below; `repullImage` pulls the image before recreation and `startAfterUpdate` starts the new container once created.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "image": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "repullImage": {
                    "type": "boolean"
                  },
                  "startAfterUpdate": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "image": "nginx:latest",
                "name": "web",
                "repullImage": true,
                "startAfterUpdate": true
              }
            }
          }
        }
      }
    },
    "/api/containers/{id}/update-runtime": {
      "post": {
        "operationId": "post_api_containers_id_update-runtime",
        "tags": [
          "containers"
        ],
        "summary": "Update a running container's restart policy and/or CPU/memory/blkio/pids limits in-place (no recreate)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Container id (from GET /api/containers)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true,
                  "warnings": [
                    "string"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid JSON body, or no supported fields were provided"
          },
          "403": {
            "description": "Permission denied"
          },
          "404": {
            "description": "Container not found"
          },
          "500": {
            "description": "Update failed"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "RestartPolicy": {
                    "type": "object",
                    "properties": {}
                  },
                  "CpuShares": {
                    "type": "integer"
                  },
                  "CpuPeriod": {
                    "type": "integer"
                  },
                  "CpuQuota": {
                    "type": "integer"
                  },
                  "CpuRealtimePeriod": {
                    "type": "integer"
                  },
                  "CpuRealtimeRuntime": {
                    "type": "integer"
                  },
                  "CpusetCpus": {
                    "type": "string"
                  },
                  "CpusetMems": {
                    "type": "string"
                  },
                  "NanoCpus": {
                    "type": "integer"
                  },
                  "Memory": {
                    "type": "integer"
                  },
                  "MemorySwap": {
                    "type": "integer"
                  },
                  "MemoryReservation": {
                    "type": "integer"
                  },
                  "MemorySwappiness": {
                    "type": "integer"
                  },
                  "KernelMemory": {
                    "type": "integer"
                  },
                  "BlkioWeight": {
                    "type": "integer"
                  },
                  "BlkioWeightDevice": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "BlkioDeviceReadBps": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "BlkioDeviceWriteBps": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "BlkioDeviceReadIOps": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "BlkioDeviceWriteIOps": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "PidsLimit": {
                    "type": "integer"
                  }
                }
              },
              "example": {
                "Memory": 536870912,
                "PidsLimit": 512
              }
            }
          }
        }
      }
    },
    "/api/containers/batch-update": {
      "post": {
        "operationId": "post_api_containers_batch-update",
        "tags": [
          "containers"
        ],
        "summary": "Recreate a set of containers with their latest images, preserving all settings (requires the 'create' permission)",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "containerId": {
                            "type": "string"
                          },
                          "containerName": {
                            "type": "string"
                          },
                          "success": {
                            "type": "boolean"
                          },
                          "error": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "containerId",
                          "containerName",
                          "success"
                        ]
                      }
                    },
                    "summary": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        },
                        "success": {
                          "type": "integer"
                        },
                        "failed": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "total",
                        "success",
                        "failed"
                      ]
                    }
                  },
                  "required": [
                    "success",
                    "results",
                    "summary"
                  ]
                },
                "example": {
                  "success": true,
                  "results": [
                    {
                      "containerId": "string",
                      "containerName": "string",
                      "success": true,
                      "error": "string"
                    }
                  ],
                  "summary": {
                    "total": 0,
                    "success": 0,
                    "failed": 0
                  }
                }
              }
            }
          },
          "400": {
            "description": "The containerIds array is missing or empty"
          },
          "403": {
            "description": "Permission denied"
          },
          "500": {
            "description": "Failed to run the batch update"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Containers are processed sequentially; the response reports per-container success/failure plus a summary. Use the streaming variant for live progress. containerIds from GET /api/containers.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "containerIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "containerIds"
                ]
              },
              "example": {
                "containerIds": [
                  "3f4a1c2b9d8e",
                  "a1b2c3d4e5f6"
                ]
              }
            }
          }
        }
      }
    },
    "/api/containers/batch-update-stream": {
      "post": {
        "operationId": "post_api_containers_batch-update-stream",
        "tags": [
          "containers"
        ],
        "summary": "Recreate a set of containers with live streaming progress (Server-Sent Events), optionally blocking on vulnerability criteria (requires the 'create' permission)",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Server-Sent Events stream (text/event-stream) of per-container update progress"
          },
          "400": {
            "description": "Invalid JSON body, or the containerIds array is missing or empty"
          },
          "403": {
            "description": "Permission denied"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Returns a `text/event-stream` reporting per-container progress. `vulnerabilityCriteria` (default \"never\") can block an update when a container's image scan exceeds the configured severity threshold. containerIds from GET /api/containers.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "containerIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "vulnerabilityCriteria": {
                    "type": "string"
                  }
                },
                "required": [
                  "containerIds"
                ]
              },
              "example": {
                "containerIds": [
                  "3f4a1c2b9d8e"
                ],
                "vulnerabilityCriteria": "never"
              }
            }
          }
        }
      }
    },
    "/api/containers/check-updates": {
      "get": {
        "operationId": "get_api_containers_check-updates",
        "tags": [
          "containers"
        ],
        "summary": "Read the cached pending image-update records for an environment (no fresh check; requires the 'view' permission)",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "environmentId": {
                      "type": "integer"
                    },
                    "pendingUpdates": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "containerId": {
                            "type": "string"
                          },
                          "containerName": {
                            "type": "string"
                          },
                          "currentImage": {
                            "type": "string"
                          },
                          "checkedAt": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "containerId",
                          "containerName",
                          "currentImage"
                        ]
                      }
                    }
                  },
                  "required": [
                    "environmentId",
                    "pendingUpdates"
                  ]
                },
                "example": {
                  "environmentId": 0,
                  "pendingUpdates": [
                    {
                      "containerId": "string",
                      "containerName": "string",
                      "currentImage": "string",
                      "checkedAt": "string"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Environment ID required"
          },
          "403": {
            "description": "Permission denied"
          },
          "500": {
            "description": "Failed to get pending updates"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Returns the containers currently flagged as having a pending image update from the last check. Does not trigger a new check — use POST to run a fresh check."
      },
      "post": {
        "operationId": "post_api_containers_check-updates",
        "tags": [
          "containers"
        ],
        "summary": "Trigger a fresh image-update check across all (non-hidden, non-podman-infra) containers in an environment",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "text/event-stream job stream (\"progress\" events with {checked,total}, final \"result\" event with {total,updatesFound,results}) — or, with \"Accept: application/json\", the final result as plain JSON"
          },
          "403": {
            "description": "Permission denied"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/containers/pending-updates": {
      "get": {
        "operationId": "get_api_containers_pending-updates",
        "tags": [
          "containers"
        ],
        "summary": "List the containers in an environment that have a pending image update recorded (requires the 'view' permission)",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (required) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "environmentId": {
                      "type": "integer"
                    },
                    "pendingUpdates": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "containerId": {
                            "type": "string"
                          },
                          "containerName": {
                            "type": "string"
                          },
                          "currentImage": {
                            "type": "string"
                          },
                          "checkedAt": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "containerId",
                          "containerName",
                          "currentImage",
                          "checkedAt"
                        ]
                      }
                    }
                  },
                  "required": [
                    "environmentId",
                    "pendingUpdates"
                  ]
                },
                "example": {
                  "environmentId": 0,
                  "pendingUpdates": [
                    {
                      "containerId": "string",
                      "containerName": "string",
                      "currentImage": "string",
                      "checkedAt": "string"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Environment ID is required"
          },
          "403": {
            "description": "Permission denied"
          },
          "500": {
            "description": "Failed to get the pending updates"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "delete": {
        "operationId": "delete_api_containers_pending-updates",
        "tags": [
          "containers"
        ],
        "summary": "Clear the pending-update record for a single container (requires the 'manage' permission)",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (required) (from GET /api/environments)"
          },
          {
            "name": "containerId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The container ID whose pending update should be cleared (required) (from GET /api/containers)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Environment ID and container ID are both required"
          },
          "403": {
            "description": "Permission denied"
          },
          "500": {
            "description": "Failed to remove the pending update"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/containers/sizes": {
      "get": {
        "operationId": "get_api_containers_sizes",
        "tags": [
          "containers"
        ],
        "summary": "List all containers in an environment together with their writable-layer and root-filesystem sizes (requires the 'view' permission)",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Array of containers with size metadata (SizeRw / SizeRootFs)"
          },
          "403": {
            "description": "Permission denied"
          },
          "500": {
            "description": "Failed to get container sizes (returns an empty object)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/containers/stats": {
      "get": {
        "operationId": "get_api_containers_stats",
        "tags": [
          "containers"
        ],
        "summary": "Return a CPU/memory/network/block-IO stats snapshot for every running container in an environment (requires the 'view' permission)",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "The target environment ID (omit for the local/default Docker host) (from GET /api/environments)"
          },
          {
            "name": "debug",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Return raw Docker stats for the single container with this name instead of the aggregate list"
          }
        ],
        "responses": {
          "200": {
            "description": "Array of per-container stats snapshots (or, with `debug`, the raw stats for one container)"
          },
          "403": {
            "description": "Permission denied"
          },
          "404": {
            "description": "The requested debug container was not found"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Returns an empty array when no environment is configured or specified. With `debug=<name>` it returns the raw memory_stats for a single container instead. On internal error it returns an empty array with status 200."
      }
    },
    "/api/containers/stats/stream": {
      "get": {
        "operationId": "get_api_containers_stats_stream",
        "tags": [
          "containers"
        ],
        "summary": "Stream live CPU/memory/network/block-IO stats for all running containers in an environment (one snapshot round, then closes)",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id — an immediate \"done\" event is sent if omitted or no environments are configured (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "text/event-stream SSE stream (\"stat\" events per container with {id,name,cpuPercent,memoryUsage,memoryRaw,memoryCache,memoryLimit,memoryPercent,networkRx,networkTx,blockRead,blockWrite}, an \"error\" event on environment-not-found, then a final \"done\" event)"
          },
          "403": {
            "description": "Permission denied"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/dashboard/preferences": {
      "get": {
        "operationId": "get_api_dashboard_preferences",
        "tags": [
          "dashboard"
        ],
        "summary": "Get the current user's dashboard preferences (grid layout, lock state, view mode)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "gridLayout": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {}
                      }
                    },
                    "locked": {
                      "type": "boolean"
                    },
                    "viewMode": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "locked",
                    "viewMode"
                  ]
                },
                "example": {
                  "gridLayout": [],
                  "locked": false,
                  "viewMode": "grid"
                }
              }
            }
          },
          "500": {
            "description": "Failed to get dashboard preferences"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_dashboard_preferences",
        "tags": [
          "dashboard"
        ],
        "summary": "Update the current user's dashboard preferences (only provided fields are merged)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "gridLayout": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {}
                      }
                    },
                    "locked": {
                      "type": "boolean"
                    },
                    "viewMode": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "locked",
                    "viewMode"
                  ]
                },
                "example": {
                  "gridLayout": [
                    {}
                  ],
                  "locked": true,
                  "viewMode": "string"
                }
              }
            }
          },
          "500": {
            "description": "Failed to save dashboard preferences"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "gridLayout": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {}
                    }
                  },
                  "locked": {
                    "type": "boolean"
                  },
                  "viewMode": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "locked": true,
                "viewMode": "list"
              }
            }
          }
        }
      }
    },
    "/api/dashboard/stats": {
      "get": {
        "operationId": "get_api_dashboard_stats",
        "tags": [
          "dashboard"
        ],
        "summary": "Get aggregated dashboard statistics per environment (containers, images, volumes, stacks, metrics)",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Restrict the stats to a single environment (returns one object) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      },
                      "name": {
                        "type": "string"
                      },
                      "online": {
                        "type": "boolean"
                      },
                      "containers": {
                        "type": "object",
                        "properties": {}
                      },
                      "images": {
                        "type": "object",
                        "properties": {}
                      },
                      "volumes": {
                        "type": "object",
                        "properties": {}
                      },
                      "networks": {
                        "type": "object",
                        "properties": {}
                      },
                      "stacks": {
                        "type": "object",
                        "properties": {}
                      },
                      "metrics": {
                        "type": "object",
                        "properties": {}
                      },
                      "events": {
                        "type": "object",
                        "properties": {}
                      }
                    },
                    "required": [
                      "id",
                      "name"
                    ]
                  }
                },
                "example": [
                  {
                    "id": 0,
                    "name": "string",
                    "online": true,
                    "containers": {},
                    "images": {},
                    "volumes": {},
                    "networks": {},
                    "stacks": {},
                    "metrics": {},
                    "events": {}
                  }
                ]
              }
            }
          },
          "403": {
            "description": "Permission denied (requires the environments:view permission)"
          },
          "404": {
            "description": "Environment not found (when env is supplied)"
          },
          "500": {
            "description": "Failed to get dashboard stats"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Returns an array of per-environment stats. When env is supplied and matches, a single object is returned instead of an array. An empty array is returned on a fresh install with no environments."
      }
    },
    "/api/dashboard/stats/stream": {
      "get": {
        "operationId": "get_api_dashboard_stats_stream",
        "tags": [
          "dashboard"
        ],
        "summary": "Stream per-environment dashboard stats progressively over Server-Sent Events",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Server-Sent Events stream (text/event-stream), or a JSON payload when Accept application/json is requested"
          },
          "403": {
            "description": "Permission denied (requires the environments:view permission)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Emits environments, partial, complete, error and done events as each environment's stats resolve. When the client sends Accept application/json, the stream is collected and returned as a single JSON payload instead."
      }
    },
    "/api/debug/memory": {
      "get": {
        "operationId": "get_api_debug_memory",
        "tags": [
          "debug"
        ],
        "summary": "Return Node.js/V8 memory diagnostics; only enabled when MEMORY_MONITOR=true. Also supports ?snapshot and ?snapshots to dump/list heap snapshots.",
        "parameters": [
          {
            "name": "gc",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Force garbage collection before reporting when true (requires Node started with --expose-gc)"
          }
        ],
        "responses": {
          "200": {
            "description": "With ?snapshot returns {snapshot}, with ?snapshots returns {snapshots}; otherwise the full memory report",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "timestamp": {
                      "type": "string"
                    },
                    "uptime": {
                      "type": "string"
                    },
                    "gcForced": {
                      "type": "boolean"
                    },
                    "gcAvailable": {
                      "type": "boolean"
                    },
                    "process": {
                      "type": "string"
                    },
                    "growth": {
                      "type": "string"
                    },
                    "v8Heap": {
                      "type": "string"
                    },
                    "system": {
                      "type": "string"
                    },
                    "rssTracker": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "timestamp",
                    "uptime",
                    "gcForced",
                    "gcAvailable",
                    "process",
                    "growth",
                    "v8Heap",
                    "system"
                  ]
                },
                "example": {
                  "timestamp": "2026-07-01T10:00:00.000Z",
                  "uptime": {
                    "ms": 3600000,
                    "hours": 1,
                    "human": "1h 0m"
                  },
                  "gcForced": false,
                  "gcAvailable": false,
                  "process": {
                    "rss": "180 MB"
                  },
                  "growth": {
                    "rssPerHour": "2 MB"
                  },
                  "v8Heap": {
                    "usedHeapSize": "90 MB"
                  },
                  "system": {
                    "cpus": 8,
                    "platform": "linux"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Memory monitor not enabled (set MEMORY_MONITOR=true)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/dependencies": {
      "get": {
        "operationId": "get_api_dependencies",
        "tags": [
          "dependencies"
        ],
        "summary": "Return the combined list of npm dependencies and external tool images (grype, trivy), sorted by name, excluding Dockhand itself",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string"
                      },
                      "version": {
                        "type": "string"
                      },
                      "license": {
                        "type": "string"
                      },
                      "repository": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "name",
                      "version"
                    ]
                  }
                },
                "example": [
                  {
                    "name": "anchore/grype",
                    "version": "v0.110.0",
                    "license": "Apache-2.0",
                    "repository": "https://github.com/anchore/grype"
                  }
                ]
              }
            }
          }
        },
        "security": []
      }
    },
    "/api/docs": {
      "get": {
        "operationId": "get_api_docs",
        "tags": [
          "docs"
        ],
        "summary": "The full OpenAPI 3.0 specification for the Dockhand REST API (unauthenticated)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The generated OpenAPI document — see GET /api/docs/ui for an interactive viewer"
          }
        },
        "security": []
      }
    },
    "/api/environments": {
      "get": {
        "operationId": "get_api_environments",
        "tags": [
          "environments"
        ],
        "summary": "List all Docker environments (hosts) known to Dockhand",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Environments accessible to the caller (filtered by RBAC in Enterprise mode; all environments in Free edition)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      },
                      "name": {
                        "type": "string"
                      },
                      "connectionType": {
                        "type": "string"
                      },
                      "host": {
                        "type": "string"
                      },
                      "port": {
                        "type": "integer"
                      },
                      "protocol": {
                        "type": "string"
                      },
                      "icon": {
                        "type": "string"
                      },
                      "publicIp": {
                        "type": "string"
                      },
                      "timezone": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "connectionType"
                    ]
                  }
                },
                "example": [
                  {
                    "id": 1,
                    "name": "hhdocker01",
                    "connectionType": "socket",
                    "host": null,
                    "port": 2375,
                    "protocol": "http",
                    "icon": "server",
                    "publicIp": "203.0.113.10",
                    "timezone": "Europe/Berlin"
                  },
                  {
                    "id": 2,
                    "name": "hhdocker02",
                    "connectionType": "hawser-edge",
                    "host": null,
                    "port": 2375,
                    "protocol": "http",
                    "icon": "server",
                    "publicIp": null,
                    "timezone": "Europe/Berlin"
                  }
                ]
              }
            }
          },
          "403": {
            "description": "Permission denied (RBAC 'environments:view' missing)"
          },
          "500": {
            "description": "Unexpected error while loading environments"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_environments",
        "tags": [
          "environments"
        ],
        "summary": "Register a new Docker environment (host)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "connectionType": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "connectionType"
                  ]
                },
                "example": {
                  "id": 3,
                  "name": "hhdocker03",
                  "connectionType": "socket"
                }
              }
            }
          },
          "400": {
            "description": "Invalid name, invalid connectionType, or host missing for direct/hawser-standard"
          },
          "403": {
            "description": "Permission denied (RBAC 'environments:create' missing)"
          },
          "409": {
            "description": "An environment with this name already exists"
          },
          "500": {
            "description": "Unexpected error while creating the environment"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "connectionType": {
                    "type": "string"
                  },
                  "host": {
                    "type": "string"
                  },
                  "port": {
                    "type": "integer"
                  },
                  "protocol": {
                    "type": "string"
                  },
                  "socketPath": {
                    "type": "string"
                  },
                  "icon": {
                    "type": "string"
                  },
                  "publicIp": {
                    "type": "string"
                  },
                  "labels": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              },
              "example": {
                "name": "hhdocker03",
                "connectionType": "socket",
                "socketPath": "/var/run/docker.sock",
                "icon": "server"
              }
            }
          }
        }
      }
    },
    "/api/environments/{id}": {
      "get": {
        "operationId": "get_api_environments_id",
        "tags": [
          "environments"
        ],
        "summary": "Get a single environment by id, including its parsed labels and public IP",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "connectionType": {
                      "type": "string"
                    },
                    "labels": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "publicIp": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "connectionType"
                  ]
                },
                "example": {
                  "id": 0,
                  "name": "string",
                  "connectionType": "string",
                  "labels": [
                    "string"
                  ],
                  "publicIp": "string"
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (RBAC 'environments:view' missing)"
          },
          "404": {
            "description": "Environment not found"
          },
          "500": {
            "description": "Unexpected error while loading the environment"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "operationId": "put_api_environments_id",
        "tags": [
          "environments"
        ],
        "summary": "Update an environment; renaming also renames its on-disk stacks/git-repos directories",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "connectionType": {
                      "type": "string"
                    },
                    "labels": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "publicIp": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "connectionType"
                  ]
                },
                "example": {
                  "id": 0,
                  "name": "string",
                  "connectionType": "string",
                  "labels": [
                    "string"
                  ],
                  "publicIp": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid new name (rename validation)"
          },
          "403": {
            "description": "Permission denied (RBAC 'environments:edit' missing)"
          },
          "404": {
            "description": "Environment not found"
          },
          "409": {
            "description": "Rename target directory already exists, or the on-disk rename failed (e.g. EXDEV across filesystems)"
          },
          "500": {
            "description": "Unexpected error while updating the environment"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "host": {
                    "type": "string"
                  },
                  "port": {
                    "type": "integer"
                  },
                  "protocol": {
                    "type": "string"
                  },
                  "tlsCa": {
                    "type": "string"
                  },
                  "tlsCert": {
                    "type": "string"
                  },
                  "tlsKey": {
                    "type": "string"
                  },
                  "tlsSkipVerify": {
                    "type": "boolean"
                  },
                  "icon": {
                    "type": "string"
                  },
                  "socketPath": {
                    "type": "string"
                  },
                  "collectActivity": {
                    "type": "boolean"
                  },
                  "collectMetrics": {
                    "type": "boolean"
                  },
                  "highlightChanges": {
                    "type": "boolean"
                  },
                  "labels": {
                    "type": "string"
                  },
                  "connectionType": {
                    "type": "string"
                  },
                  "hawserToken": {
                    "type": "string"
                  },
                  "publicIp": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "name": "hhdocker03",
                "collectMetrics": true
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_environments_id",
        "tags": [
          "environments"
        ],
        "summary": "Delete an environment and all its associated git stacks, schedules, icons, and on-disk directories",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid environment id, or the environment could not be deleted"
          },
          "403": {
            "description": "Permission denied (RBAC 'environments:delete' missing)"
          },
          "404": {
            "description": "Environment not found"
          },
          "500": {
            "description": "Environment name is empty/whitespace (refuses to delete to avoid an unsafe directory cleanup), or an unexpected error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/environments/{id}/disk-warning": {
      "get": {
        "operationId": "get_api_environments_id_disk-warning",
        "tags": [
          "environments"
        ],
        "summary": "Get the disk-space warning thresholds for an environment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enabled": {
                      "type": "boolean"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "threshold": {
                      "type": "integer"
                    },
                    "thresholdGb": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "enabled",
                    "mode",
                    "threshold",
                    "thresholdGb"
                  ]
                },
                "example": {
                  "enabled": true,
                  "mode": "percentage",
                  "threshold": 80,
                  "thresholdGb": 50
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (RBAC 'environments:view' missing)"
          },
          "404": {
            "description": "Environment not found"
          },
          "500": {
            "description": "Unexpected error while loading the settings"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_environments_id_disk-warning",
        "tags": [
          "environments"
        ],
        "summary": "Save the disk-space warning thresholds for an environment (each field optional/independent)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (RBAC 'environments:edit' missing)"
          },
          "404": {
            "description": "Environment not found"
          },
          "500": {
            "description": "Unexpected error while saving the settings"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "mode": {
                    "type": "string"
                  },
                  "threshold": {
                    "type": "integer"
                  },
                  "thresholdGb": {
                    "type": "integer"
                  }
                }
              },
              "example": {
                "enabled": true,
                "mode": "percentage",
                "threshold": 85
              }
            }
          }
        }
      }
    },
    "/api/environments/{id}/icon": {
      "get": {
        "operationId": "get_api_environments_id_icon",
        "tags": [
          "environments"
        ],
        "summary": "Get the custom icon image for an environment (raw image/webp bytes, not JSON)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Binary image/webp response body, Cache-Control public max-age=3600"
          },
          "404": {
            "description": "No custom icon set for this environment"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_environments_id_icon",
        "tags": [
          "environments"
        ],
        "summary": "Upload a custom icon for an environment (base64-encoded image, ~200KB limit)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "icon": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success",
                    "icon"
                  ]
                },
                "example": {
                  "success": true,
                  "icon": "custom:env-3.webp"
                }
              }
            }
          },
          "400": {
            "description": "Missing image data, or image exceeds the ~300000-char base64 size limit"
          },
          "403": {
            "description": "Permission denied (RBAC 'environments:edit' missing)"
          },
          "404": {
            "description": "Environment not found"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "image": {
                    "type": "string"
                  }
                },
                "required": [
                  "image"
                ]
              },
              "example": {
                "image": "string"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_environments_id_icon",
        "tags": [
          "environments"
        ],
        "summary": "Remove an environment's custom icon and reset it to the default \"globe\" icon",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "icon": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success",
                    "icon"
                  ]
                },
                "example": {
                  "success": true,
                  "icon": "globe"
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (RBAC 'environments:edit' missing)"
          },
          "404": {
            "description": "Environment not found"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/environments/{id}/image-prune": {
      "get": {
        "operationId": "get_api_environments_id_image-prune",
        "tags": [
          "environments"
        ],
        "summary": "Get the automatic image-prune schedule settings for an environment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "settings": {
                      "type": "object",
                      "properties": {
                        "enabled": {
                          "type": "boolean"
                        },
                        "cronExpression": {
                          "type": "string"
                        },
                        "pruneMode": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "enabled",
                        "cronExpression",
                        "pruneMode"
                      ]
                    }
                  },
                  "required": [
                    "settings"
                  ]
                },
                "example": {
                  "settings": {
                    "enabled": false,
                    "cronExpression": "0 3 * * 0",
                    "pruneMode": "dangling"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (RBAC 'environments:view' missing)"
          },
          "404": {
            "description": "Environment not found"
          },
          "500": {
            "description": "Unexpected error while loading the settings"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_environments_id_image-prune",
        "tags": [
          "environments"
        ],
        "summary": "Save the automatic image-prune schedule for an environment (registers/unregisters the croner job)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "settings": {
                      "type": "object",
                      "properties": {
                        "enabled": {
                          "type": "boolean"
                        },
                        "cronExpression": {
                          "type": "string"
                        },
                        "pruneMode": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "enabled",
                        "cronExpression",
                        "pruneMode"
                      ]
                    }
                  },
                  "required": [
                    "success",
                    "settings"
                  ]
                },
                "example": {
                  "success": true,
                  "settings": {
                    "enabled": true,
                    "cronExpression": "string",
                    "pruneMode": "string"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (RBAC 'environments:edit' missing)"
          },
          "404": {
            "description": "Environment not found"
          },
          "500": {
            "description": "Unexpected error while saving the settings"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "cronExpression": {
                    "type": "string"
                  },
                  "pruneMode": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "enabled": true,
                "cronExpression": "0 3 * * 0",
                "pruneMode": "dangling"
              }
            }
          }
        }
      },
      "put": {
        "operationId": "put_api_environments_id_image-prune",
        "tags": [
          "environments"
        ],
        "summary": "Immediately run an image prune for an environment (outside its schedule)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "The prune operation itself failed (Docker error)"
          },
          "403": {
            "description": "Permission denied (RBAC 'environments:edit' missing)"
          },
          "404": {
            "description": "Environment not found"
          },
          "500": {
            "description": "Unexpected error while triggering the prune"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/environments/{id}/notifications": {
      "get": {
        "operationId": "get_api_environments_id_notifications",
        "tags": [
          "environments"
        ],
        "summary": "List notification channel configurations attached to an environment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      },
                      "notificationId": {
                        "type": "integer"
                      },
                      "enabled": {
                        "type": "boolean"
                      },
                      "eventTypes": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    },
                    "required": [
                      "id",
                      "notificationId",
                      "enabled"
                    ]
                  }
                },
                "example": [
                  {
                    "id": 0,
                    "notificationId": 0,
                    "enabled": true,
                    "eventTypes": [
                      "string"
                    ]
                  }
                ]
              }
            }
          },
          "400": {
            "description": "Invalid environment id"
          },
          "403": {
            "description": "Permission denied (RBAC 'notifications:view' missing)"
          },
          "404": {
            "description": "Environment not found"
          },
          "500": {
            "description": "Unexpected error while loading notifications"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_environments_id_notifications",
        "tags": [
          "environments"
        ],
        "summary": "Attach a notification channel to an environment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "notificationId": {
                      "type": "integer"
                    },
                    "enabled": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "id",
                    "notificationId",
                    "enabled"
                  ]
                },
                "example": {
                  "id": 0,
                  "notificationId": 0,
                  "enabled": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid environment id, or notificationId missing"
          },
          "403": {
            "description": "Permission denied (RBAC 'notifications:edit' missing)"
          },
          "404": {
            "description": "Environment not found"
          },
          "409": {
            "description": "This notification channel is already configured for this environment"
          },
          "500": {
            "description": "Unexpected error while creating the notification link"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "notificationId from GET /api/notifications.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "notificationId": {
                    "type": "integer"
                  },
                  "enabled": {
                    "type": "boolean"
                  },
                  "eventTypes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "notificationId"
                ]
              },
              "example": {
                "notificationId": 1,
                "enabled": true,
                "eventTypes": [
                  "stack_deploy_failed"
                ]
              }
            }
          }
        }
      }
    },
    "/api/environments/{id}/notifications/{notificationId}": {
      "get": {
        "operationId": "get_api_environments_id_notifications_notificationId",
        "tags": [
          "environments"
        ],
        "summary": "Get one notification-channel link for an environment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          },
          {
            "name": "notificationId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Notification channel id (from GET /api/notifications)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "notificationId": {
                      "type": "integer"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "eventTypes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "id",
                    "notificationId",
                    "enabled"
                  ]
                },
                "example": {
                  "id": 0,
                  "notificationId": 0,
                  "enabled": true,
                  "eventTypes": [
                    "string"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid id or notificationId"
          },
          "403": {
            "description": "Permission denied (RBAC 'notifications:view' missing)"
          },
          "404": {
            "description": "Environment not found, or no such notification link for this environment"
          },
          "500": {
            "description": "Unexpected error while loading the notification link"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "operationId": "put_api_environments_id_notifications_notificationId",
        "tags": [
          "environments"
        ],
        "summary": "Update a notification-channel link (enabled state and/or event type filter)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          },
          {
            "name": "notificationId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Notification channel id (from GET /api/notifications)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "notificationId": {
                      "type": "integer"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "eventTypes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "id",
                    "notificationId",
                    "enabled"
                  ]
                },
                "example": {
                  "id": 0,
                  "notificationId": 0,
                  "enabled": true,
                  "eventTypes": [
                    "string"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid id or notificationId"
          },
          "403": {
            "description": "Permission denied (RBAC 'notifications:edit' missing)"
          },
          "404": {
            "description": "Environment not found, or no such notification link for this environment"
          },
          "500": {
            "description": "Unexpected error while updating the notification link"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "eventTypes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "example": {
                "enabled": false
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_environments_id_notifications_notificationId",
        "tags": [
          "environments"
        ],
        "summary": "Remove a notification-channel link from an environment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          },
          {
            "name": "notificationId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Notification channel id (from GET /api/notifications)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid id or notificationId"
          },
          "403": {
            "description": "Permission denied (RBAC 'notifications:delete' missing)"
          },
          "404": {
            "description": "No such notification link for this environment"
          },
          "500": {
            "description": "Unexpected error while deleting the notification link"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/environments/{id}/remote-stacks-dir": {
      "get": {
        "operationId": "get_api_environments_id_remote-stacks-dir",
        "tags": [
          "environments"
        ],
        "summary": "Get the per-environment remote stacks directory used to stage stack files onto a direct (agentless) daemon",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "remoteStacksDir": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "remoteStacksDir": "/mnt/dockhand/stacks"
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (requires environments:view)"
          },
          "404": {
            "description": "Environment not found"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_environments_id_remote-stacks-dir",
        "tags": [
          "environments"
        ],
        "summary": "Set (or clear) the per-environment remote stacks directory for a direct (agentless) daemon",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "remoteStacksDir": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true,
                  "remoteStacksDir": "/mnt/dockhand/stacks"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input — remoteStacksDir must be a string or null, or must be an absolute path with no \"..\""
          },
          "403": {
            "description": "Permission denied (requires environments:edit)"
          },
          "404": {
            "description": "Environment not found"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "remoteStacksDir": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "remoteStacksDir": "/mnt/dockhand/stacks"
              }
            }
          }
        }
      }
    },
    "/api/environments/{id}/test": {
      "post": {
        "operationId": "post_api_environments_id_test",
        "tags": [
          "environments"
        ],
        "summary": "Test connectivity to a saved environment's Docker/Hawser endpoint",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "success:false with a human-readable error message is also returned as HTTP 200 (connection/agent-not-connected states are not transport errors)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "info": {
                      "type": "object",
                      "properties": {
                        "serverVersion": {
                          "type": "string"
                        },
                        "containers": {
                          "type": "integer"
                        },
                        "images": {
                          "type": "integer"
                        },
                        "name": {
                          "type": "string"
                        },
                        "engine": {
                          "type": "string"
                        }
                      }
                    },
                    "isEdgeMode": {
                      "type": "boolean"
                    },
                    "hawser": {
                      "type": "object",
                      "properties": {}
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true,
                  "info": {
                    "serverVersion": "string",
                    "containers": 0,
                    "images": 0,
                    "name": "string",
                    "engine": "string"
                  },
                  "isEdgeMode": true,
                  "hawser": {}
                }
              }
            }
          },
          "404": {
            "description": "Environment not found"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/environments/{id}/timezone": {
      "get": {
        "operationId": "get_api_environments_id_timezone",
        "tags": [
          "environments"
        ],
        "summary": "Get the IANA timezone used for scheduling on an environment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "timezone": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "timezone"
                  ]
                },
                "example": {
                  "timezone": "Europe/Berlin"
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (RBAC 'environments:view' missing)"
          },
          "404": {
            "description": "Environment not found"
          },
          "500": {
            "description": "Unexpected error while loading the timezone"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_environments_id_timezone",
        "tags": [
          "environments"
        ],
        "summary": "Set the IANA timezone for an environment and refresh its schedules to use it",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "timezone": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success",
                    "timezone"
                  ]
                },
                "example": {
                  "success": true,
                  "timezone": "string"
                }
              }
            }
          },
          "400": {
            "description": "Not a recognized IANA timezone (Intl.supportedValuesOf('timeZone'))"
          },
          "403": {
            "description": "Permission denied (RBAC 'environments:edit' missing)"
          },
          "404": {
            "description": "Environment not found"
          },
          "500": {
            "description": "Unexpected error while saving the timezone"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "timezone": {
                    "type": "string"
                  }
                },
                "required": [
                  "timezone"
                ]
              },
              "example": {
                "timezone": "Europe/Berlin"
              }
            }
          }
        }
      }
    },
    "/api/environments/{id}/update-check": {
      "get": {
        "operationId": "get_api_environments_id_update-check",
        "tags": [
          "environments"
        ],
        "summary": "Get the automatic container-image update-check schedule for an environment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "settings": {
                      "type": "object",
                      "properties": {
                        "enabled": {
                          "type": "boolean"
                        },
                        "cron": {
                          "type": "string"
                        },
                        "autoUpdate": {
                          "type": "boolean"
                        },
                        "vulnerabilityCriteria": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "enabled",
                        "cron",
                        "autoUpdate",
                        "vulnerabilityCriteria"
                      ]
                    }
                  },
                  "required": [
                    "settings"
                  ]
                },
                "example": {
                  "settings": {
                    "enabled": false,
                    "cron": "0 4 * * *",
                    "autoUpdate": false,
                    "vulnerabilityCriteria": "never"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (RBAC 'environments:view' missing)"
          },
          "404": {
            "description": "Environment not found"
          },
          "500": {
            "description": "Unexpected error while loading the settings"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_environments_id_update-check",
        "tags": [
          "environments"
        ],
        "summary": "Save the automatic image update-check schedule for an environment (registers/unregisters the croner job)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "settings": {
                      "type": "object",
                      "properties": {
                        "enabled": {
                          "type": "boolean"
                        },
                        "cron": {
                          "type": "string"
                        },
                        "autoUpdate": {
                          "type": "boolean"
                        },
                        "vulnerabilityCriteria": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "enabled",
                        "cron",
                        "autoUpdate",
                        "vulnerabilityCriteria"
                      ]
                    }
                  },
                  "required": [
                    "success",
                    "settings"
                  ]
                },
                "example": {
                  "success": true,
                  "settings": {
                    "enabled": true,
                    "cron": "string",
                    "autoUpdate": true,
                    "vulnerabilityCriteria": "string"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (RBAC 'environments:edit' missing)"
          },
          "404": {
            "description": "Environment not found"
          },
          "500": {
            "description": "Unexpected error while saving the settings"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "cron": {
                    "type": "string"
                  },
                  "autoUpdate": {
                    "type": "boolean"
                  },
                  "vulnerabilityCriteria": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "enabled": true,
                "cron": "0 4 * * *",
                "autoUpdate": false,
                "vulnerabilityCriteria": "never"
              }
            }
          }
        }
      }
    },
    "/api/environments/detect-socket": {
      "get": {
        "operationId": "get_api_environments_detect-socket",
        "tags": [
          "environments"
        ],
        "summary": "Detect common Docker/Podman socket paths that exist on the Dockhand host",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sockets": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "path": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "exists": {
                            "type": "boolean"
                          }
                        },
                        "required": [
                          "path",
                          "name",
                          "exists"
                        ]
                      }
                    },
                    "homedir": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "sockets",
                    "homedir"
                  ]
                },
                "example": {
                  "sockets": [
                    {
                      "path": "/var/run/docker.sock",
                      "name": "Docker (default)",
                      "exists": true
                    }
                  ],
                  "homedir": "/home/dockhand"
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/environments/test": {
      "post": {
        "operationId": "post_api_environments_test",
        "tags": [
          "environments"
        ],
        "summary": "Test a Docker/Hawser connection configuration WITHOUT saving it as an environment",
        "parameters": [],
        "responses": {
          "200": {
            "description": "success:false with a human-readable error message is also returned as HTTP 200 (connection failures are not transport errors)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "info": {
                      "type": "object",
                      "properties": {
                        "serverVersion": {
                          "type": "string"
                        },
                        "containers": {
                          "type": "integer"
                        },
                        "images": {
                          "type": "integer"
                        },
                        "name": {
                          "type": "string"
                        }
                      }
                    },
                    "hawser": {
                      "type": "object",
                      "properties": {}
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true,
                  "info": {
                    "serverVersion": "string",
                    "containers": 0,
                    "images": 0,
                    "name": "string"
                  },
                  "hawser": {}
                }
              }
            }
          },
          "400": {
            "description": "Host is required for direct/hawser-standard connection types"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "connectionType": {
                    "type": "string"
                  },
                  "socketPath": {
                    "type": "string"
                  },
                  "host": {
                    "type": "string"
                  },
                  "port": {
                    "type": "integer"
                  },
                  "protocol": {
                    "type": "string"
                  },
                  "tlsCa": {
                    "type": "string"
                  },
                  "tlsCert": {
                    "type": "string"
                  },
                  "tlsKey": {
                    "type": "string"
                  },
                  "tlsSkipVerify": {
                    "type": "boolean"
                  },
                  "hawserToken": {
                    "type": "string"
                  }
                },
                "required": [
                  "connectionType"
                ]
              },
              "example": {
                "connectionType": "socket",
                "socketPath": "/var/run/docker.sock"
              }
            }
          }
        }
      }
    },
    "/api/events": {
      "get": {
        "operationId": "get_api_events",
        "tags": [
          "events"
        ],
        "summary": "Stream live Docker events (container/image/volume/network) for an environment via SSE, with periodic heartbeats",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id — without it, an \"info\" SSE message is sent and the stream ends (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "text/event-stream SSE stream (\"connected\", \"heartbeat\" every 5s, \"docker\" events with {type,action,actor,time,timeNano}, or an \"error\"/\"info\" event for edge environments, missing/unknown environment, or a lost Docker connection)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/git/credentials": {
      "get": {
        "operationId": "get_api_git_credentials",
        "tags": [
          "git"
        ],
        "summary": "List all stored git credentials with secrets stripped (only hasPassword/hasSshKey flags returned)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      },
                      "name": {
                        "type": "string"
                      },
                      "authType": {
                        "type": "string"
                      },
                      "username": {
                        "type": "string"
                      },
                      "hasPassword": {
                        "type": "boolean"
                      },
                      "hasSshKey": {
                        "type": "boolean"
                      },
                      "createdAt": {
                        "type": "string"
                      },
                      "updatedAt": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "authType",
                      "hasPassword",
                      "hasSshKey"
                    ]
                  }
                },
                "example": [
                  {
                    "id": 1,
                    "name": "github-deploy",
                    "authType": "ssh",
                    "username": "git",
                    "hasPassword": false,
                    "hasSshKey": true,
                    "createdAt": "2026-06-01T10:00:00Z",
                    "updatedAt": "2026-06-01T10:00:00Z"
                  }
                ]
              }
            }
          },
          "403": {
            "description": "Caller lacks the git:view permission"
          },
          "500": {
            "description": "Failed to read git credentials from the database"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_git_credentials",
        "tags": [
          "git"
        ],
        "summary": "Create a new git credential (none/password/ssh auth) and return it with secrets stripped",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "authType": {
                      "type": "string"
                    },
                    "username": {
                      "type": "string"
                    },
                    "hasPassword": {
                      "type": "boolean"
                    },
                    "hasSshKey": {
                      "type": "boolean"
                    },
                    "createdAt": {
                      "type": "string"
                    },
                    "updatedAt": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "authType",
                    "hasPassword",
                    "hasSshKey"
                  ]
                },
                "example": {
                  "id": 0,
                  "name": "string",
                  "authType": "string",
                  "username": "string",
                  "hasPassword": true,
                  "hasSshKey": true,
                  "createdAt": "string",
                  "updatedAt": "string"
                }
              }
            }
          },
          "400": {
            "description": "Missing name, invalid authType, a missing secret for the chosen authType, or a duplicate credential name"
          },
          "403": {
            "description": "Caller lacks the git:create permission"
          },
          "500": {
            "description": "Failed to create the git credential"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "authType": {
                    "type": "string"
                  },
                  "username": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  },
                  "sshPrivateKey": {
                    "type": "string"
                  },
                  "sshPassphrase": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              },
              "example": {
                "name": "github-deploy",
                "authType": "ssh",
                "username": "git",
                "sshPrivateKey": "***",
                "sshPassphrase": "***"
              }
            }
          }
        }
      }
    },
    "/api/git/credentials/{id}": {
      "get": {
        "operationId": "get_api_git_credentials_id",
        "tags": [
          "git"
        ],
        "summary": "Get a single git credential by ID with secrets stripped (only hasPassword/hasSshKey flags returned)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git credential ID (from GET /api/git/credentials)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "authType": {
                      "type": "string"
                    },
                    "username": {
                      "type": "string"
                    },
                    "hasPassword": {
                      "type": "boolean"
                    },
                    "hasSshKey": {
                      "type": "boolean"
                    },
                    "createdAt": {
                      "type": "string"
                    },
                    "updatedAt": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "authType",
                    "hasPassword",
                    "hasSshKey"
                  ]
                },
                "example": {
                  "id": 0,
                  "name": "string",
                  "authType": "string",
                  "username": "string",
                  "hasPassword": true,
                  "hasSshKey": true,
                  "createdAt": "string",
                  "updatedAt": "string"
                }
              }
            }
          },
          "400": {
            "description": "The id path segment is not a valid integer"
          },
          "403": {
            "description": "Caller lacks the git:view permission"
          },
          "404": {
            "description": "No credential exists with that ID"
          },
          "500": {
            "description": "Failed to read the git credential"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "operationId": "put_api_git_credentials_id",
        "tags": [
          "git"
        ],
        "summary": "Update a git credential and return it with secrets stripped",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git credential ID (from GET /api/git/credentials)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "authType": {
                      "type": "string"
                    },
                    "username": {
                      "type": "string"
                    },
                    "hasPassword": {
                      "type": "boolean"
                    },
                    "hasSshKey": {
                      "type": "boolean"
                    },
                    "createdAt": {
                      "type": "string"
                    },
                    "updatedAt": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "authType",
                    "hasPassword",
                    "hasSshKey"
                  ]
                },
                "example": {
                  "id": 0,
                  "name": "string",
                  "authType": "string",
                  "username": "string",
                  "hasPassword": true,
                  "hasSshKey": true,
                  "createdAt": "string",
                  "updatedAt": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid id, invalid authType, or a duplicate credential name"
          },
          "403": {
            "description": "Caller lacks the git:edit permission"
          },
          "404": {
            "description": "No credential exists with that ID"
          },
          "500": {
            "description": "The update failed or the credential could not be persisted"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "authType": {
                    "type": "string"
                  },
                  "username": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  },
                  "sshPrivateKey": {
                    "type": "string"
                  },
                  "sshPassphrase": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "name": "github-deploy",
                "authType": "password",
                "username": "git",
                "password": "***"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_git_credentials_id",
        "tags": [
          "git"
        ],
        "summary": "Delete a git credential by ID",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git credential ID (from GET /api/git/credentials)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "The id path segment is not a valid integer"
          },
          "403": {
            "description": "Caller lacks the git:delete permission"
          },
          "404": {
            "description": "No credential exists with that ID"
          },
          "500": {
            "description": "The deletion failed"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/git/preview-env": {
      "post": {
        "operationId": "post_api_git_preview-env",
        "tags": [
          "git"
        ],
        "summary": "Clone a repo to a temp dir and preview its merged env-file variables for the git-stack env editor",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "vars": {
                      "type": "string"
                    },
                    "sources": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "vars",
                    "sources"
                  ]
                },
                "example": {
                  "vars": "string",
                  "sources": "string"
                }
              }
            }
          },
          "400": {
            "description": "composePath missing, neither repositoryId nor url supplied, or the repo/env-file preview reported an error"
          },
          "401": {
            "description": "Authentication required"
          },
          "404": {
            "description": "The referenced repository does not exist"
          },
          "500": {
            "description": "Failed to preview the env files (clone or read error)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "repositoryId from GET /api/git/repositories. credentialId from GET /api/git/credentials.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "repositoryId": {
                    "type": "integer"
                  },
                  "url": {
                    "type": "string"
                  },
                  "branch": {
                    "type": "string"
                  },
                  "credentialId": {
                    "type": "integer"
                  },
                  "composePath": {
                    "type": "string"
                  },
                  "envFilePath": {
                    "type": "string"
                  }
                },
                "required": [
                  "composePath"
                ]
              },
              "example": {
                "repositoryId": 3,
                "composePath": "docker-compose.yml",
                "envFilePath": ".env.prod"
              }
            }
          }
        }
      }
    },
    "/api/git/repositories": {
      "get": {
        "operationId": "get_api_git_repositories",
        "tags": [
          "git"
        ],
        "summary": "List all git repositories (repositories are global, not scoped to an environment)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      },
                      "name": {
                        "type": "string"
                      },
                      "url": {
                        "type": "string"
                      },
                      "branch": {
                        "type": "string"
                      },
                      "credentialId": {
                        "type": "integer"
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "url",
                      "branch"
                    ]
                  }
                },
                "example": [
                  {
                    "id": 1,
                    "name": "homelab",
                    "url": "https://github.com/example/homelab.git",
                    "branch": "main",
                    "credentialId": 2
                  }
                ]
              }
            }
          },
          "403": {
            "description": "Caller lacks the git:view permission"
          },
          "500": {
            "description": "Failed to read git repositories"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_git_repositories",
        "tags": [
          "git"
        ],
        "summary": "Create a git repository (branch defaults to main); deployment config lives on git stacks, not here",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "branch": {
                      "type": "string"
                    },
                    "credentialId": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "url",
                    "branch"
                  ]
                },
                "example": {
                  "id": 0,
                  "name": "string",
                  "url": "string",
                  "branch": "string",
                  "credentialId": 0
                }
              }
            }
          },
          "400": {
            "description": "Missing name/url, an invalid credentialId, or a duplicate repository name"
          },
          "403": {
            "description": "Caller lacks the git:create permission"
          },
          "500": {
            "description": "Failed to create the git repository"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "credentialId from GET /api/git/credentials.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "url": {
                    "type": "string"
                  },
                  "branch": {
                    "type": "string"
                  },
                  "credentialId": {
                    "type": "integer"
                  }
                },
                "required": [
                  "name",
                  "url"
                ]
              },
              "example": {
                "name": "homelab",
                "url": "https://github.com/example/homelab.git",
                "branch": "main",
                "credentialId": 2
              }
            }
          }
        }
      }
    },
    "/api/git/repositories/{id}": {
      "get": {
        "operationId": "get_api_git_repositories_id",
        "tags": [
          "git"
        ],
        "summary": "Get a single git repository by ID",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git repository ID (from GET /api/git/repositories)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "branch": {
                      "type": "string"
                    },
                    "credentialId": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "url",
                    "branch"
                  ]
                },
                "example": {
                  "id": 0,
                  "name": "string",
                  "url": "string",
                  "branch": "string",
                  "credentialId": 0
                }
              }
            }
          },
          "400": {
            "description": "The id path segment is not a valid integer"
          },
          "403": {
            "description": "Caller lacks the git:view permission"
          },
          "404": {
            "description": "No repository exists with that ID"
          },
          "500": {
            "description": "Failed to read the git repository"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "operationId": "put_api_git_repositories_id",
        "tags": [
          "git"
        ],
        "summary": "Update a git repository's basic fields (name/url/branch/credential)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git repository ID (from GET /api/git/repositories)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "branch": {
                      "type": "string"
                    },
                    "credentialId": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "url",
                    "branch"
                  ]
                },
                "example": {
                  "id": 0,
                  "name": "string",
                  "url": "string",
                  "branch": "string",
                  "credentialId": 0
                }
              }
            }
          },
          "400": {
            "description": "Invalid id, an invalid credentialId, or a duplicate repository name"
          },
          "403": {
            "description": "Caller lacks the git:edit permission"
          },
          "404": {
            "description": "No repository exists with that ID"
          },
          "500": {
            "description": "The update failed or the repository could not be persisted"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "credentialId from GET /api/git/credentials.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "url": {
                    "type": "string"
                  },
                  "branch": {
                    "type": "string"
                  },
                  "credentialId": {
                    "type": "integer"
                  }
                }
              },
              "example": {
                "name": "homelab",
                "url": "https://github.com/example/homelab.git",
                "branch": "production",
                "credentialId": 2
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_git_repositories_id",
        "tags": [
          "git"
        ],
        "summary": "Delete a git repository, first removing the clone directories of every git stack it backs",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git repository ID (from GET /api/git/repositories)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "The id path segment is not a valid integer"
          },
          "403": {
            "description": "Caller lacks the git:delete permission"
          },
          "404": {
            "description": "No repository exists with that ID"
          },
          "500": {
            "description": "The deletion failed"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/git/repositories/{id}/deploy": {
      "post": {
        "operationId": "post_api_git_repositories_id_deploy",
        "tags": [
          "git"
        ],
        "summary": "Deploy the compose stack(s) defined in a git repository (clones/pulls, then runs docker compose)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git repository ID (from GET /api/git/repositories)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "The id path segment is not a valid integer"
          },
          "404": {
            "description": "No repository exists with that ID"
          },
          "500": {
            "description": "The deployment failed"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/git/repositories/{id}/sync": {
      "get": {
        "operationId": "get_api_git_repositories_id_sync",
        "tags": [
          "git"
        ],
        "summary": "Check whether the tracked branch has new commits upstream without pulling them",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git repository ID (from GET /api/git/repositories)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "hasUpdates": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "hasUpdates"
                  ]
                },
                "example": {
                  "hasUpdates": false
                }
              }
            }
          },
          "400": {
            "description": "The id path segment is not a valid integer"
          },
          "404": {
            "description": "No repository exists with that ID"
          },
          "500": {
            "description": "The update check failed"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_git_repositories_id_sync",
        "tags": [
          "git"
        ],
        "summary": "Sync (git pull) the local clone of a repository to the latest commit on its tracked branch",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git repository ID (from GET /api/git/repositories)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "The id path segment is not a valid integer"
          },
          "404": {
            "description": "No repository exists with that ID"
          },
          "500": {
            "description": "The sync failed"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/git/repositories/{id}/test": {
      "post": {
        "operationId": "post_api_git_repositories_id_test",
        "tags": [
          "git"
        ],
        "summary": "Test connectivity/authentication to a saved repository using its stored credential",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git repository ID (from GET /api/git/repositories)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "The id path segment is not a valid integer"
          },
          "404": {
            "description": "No repository exists with that ID"
          },
          "500": {
            "description": "The connectivity test failed"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/git/repositories/test": {
      "post": {
        "operationId": "post_api_git_repositories_test",
        "tags": [
          "git"
        ],
        "summary": "Test an unsaved repository configuration (url/branch/credentialId) before creating it",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "The url field is missing"
          },
          "403": {
            "description": "Caller lacks the settings:manage permission"
          },
          "500": {
            "description": "The connectivity test failed"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "credentialId from GET /api/git/credentials.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string"
                  },
                  "branch": {
                    "type": "string"
                  },
                  "credentialId": {
                    "type": "integer"
                  }
                },
                "required": [
                  "url"
                ]
              },
              "example": {
                "url": "https://github.com/example/homelab.git",
                "branch": "main",
                "credentialId": 2
              }
            }
          }
        }
      }
    },
    "/api/git/stacks": {
      "get": {
        "operationId": "get_api_git_stacks",
        "tags": [
          "git"
        ],
        "summary": "List git-backed stacks (optionally scoped to one environment)",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id — omitted returns stacks across all environments plus legacy null-scoped ones (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      },
                      "stackName": {
                        "type": "string"
                      },
                      "environmentId": {
                        "type": "integer"
                      },
                      "repositoryId": {
                        "type": "integer"
                      },
                      "composePath": {
                        "type": "string"
                      },
                      "autoUpdate": {
                        "type": "boolean"
                      },
                      "syncStatus": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "id",
                      "stackName",
                      "repositoryId",
                      "composePath",
                      "autoUpdate",
                      "syncStatus"
                    ]
                  }
                },
                "example": [
                  {
                    "id": 1,
                    "stackName": "immich",
                    "environmentId": 2,
                    "repositoryId": 3,
                    "composePath": "compose.yaml",
                    "autoUpdate": true,
                    "syncStatus": "synced"
                  }
                ]
              }
            }
          },
          "403": {
            "description": "Permission denied (RBAC 'stacks:view' missing)"
          },
          "500": {
            "description": "Unexpected error while loading git stacks"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_git_stacks",
        "tags": [
          "git"
        ],
        "summary": "Create a git-backed stack (creating the repository too if url is given instead of repositoryId)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "With deployNow:true this is the FINAL result of an SSE job stream (includes a nested deployResult); without it, the created record is returned immediately.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "stackName": {
                      "type": "string"
                    },
                    "environmentId": {
                      "type": "integer"
                    },
                    "repositoryId": {
                      "type": "integer"
                    },
                    "syncStatus": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "stackName",
                    "repositoryId",
                    "syncStatus"
                  ]
                },
                "example": {
                  "id": 7,
                  "stackName": "immich",
                  "environmentId": 2,
                  "repositoryId": 3,
                  "syncStatus": "pending"
                }
              }
            }
          },
          "400": {
            "description": "Invalid stackName/url, duplicate environment-variable keys, or repository/credential not found"
          },
          "403": {
            "description": "Permission denied (RBAC 'stacks:create' missing)"
          },
          "409": {
            "description": "A stack with this name already exists on this environment"
          },
          "500": {
            "description": "Unexpected error while creating the git stack"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "environmentId from GET /api/environments. repositoryId from GET /api/git/repositories. credentialId from GET /api/git/credentials.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "stackName": {
                    "type": "string"
                  },
                  "environmentId": {
                    "type": "integer"
                  },
                  "repositoryId": {
                    "type": "integer"
                  },
                  "url": {
                    "type": "string"
                  },
                  "branch": {
                    "type": "string"
                  },
                  "credentialId": {
                    "type": "integer"
                  },
                  "composePath": {
                    "type": "string"
                  },
                  "envFilePath": {
                    "type": "string"
                  },
                  "autoUpdate": {
                    "type": "boolean"
                  },
                  "autoUpdateCron": {
                    "type": "string"
                  },
                  "webhookEnabled": {
                    "type": "boolean"
                  },
                  "envVars": {
                    "type": "string"
                  },
                  "deployNow": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "stackName"
                ]
              },
              "example": {
                "stackName": "immich",
                "environmentId": 2,
                "url": "https://git.example.com/infra/immich.git",
                "branch": "main",
                "composePath": "compose.yaml",
                "deployNow": true,
                "envVars": [
                  {
                    "key": "TZ",
                    "value": "Europe/Berlin",
                    "isSecret": false
                  }
                ]
              }
            }
          }
        }
      }
    },
    "/api/git/stacks/{id}": {
      "get": {
        "operationId": "get_api_git_stacks_id",
        "tags": [
          "git"
        ],
        "summary": "Get a single git stack by its numeric id",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git stack id (from GET /api/git/stacks)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "stackName": {
                      "type": "string"
                    },
                    "environmentId": {
                      "type": "integer"
                    },
                    "repositoryId": {
                      "type": "integer"
                    },
                    "composePath": {
                      "type": "string"
                    },
                    "syncStatus": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "stackName",
                    "repositoryId",
                    "composePath",
                    "syncStatus"
                  ]
                },
                "example": {
                  "id": 7,
                  "stackName": "immich",
                  "environmentId": 2,
                  "repositoryId": 3,
                  "composePath": "compose.yaml",
                  "syncStatus": "synced"
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (RBAC 'stacks:view' missing)"
          },
          "404": {
            "description": "Git stack not found"
          },
          "500": {
            "description": "Unexpected error while loading the git stack"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "operationId": "put_api_git_stacks_id",
        "tags": [
          "git"
        ],
        "summary": "Update a git stack's configuration; optionally save env-var overrides and/or redeploy",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git stack id (from GET /api/git/stacks)"
          }
        ],
        "responses": {
          "200": {
            "description": "With deployNow:true this is the final SSE-job result (includes a nested deployResult).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "stackName": {
                      "type": "string"
                    },
                    "environmentId": {
                      "type": "integer"
                    },
                    "syncStatus": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "stackName",
                    "syncStatus"
                  ]
                },
                "example": {
                  "id": 7,
                  "stackName": "immich",
                  "environmentId": 2,
                  "syncStatus": "synced"
                }
              }
            }
          },
          "400": {
            "description": "Invalid stackName format, or duplicate environment-variable keys"
          },
          "403": {
            "description": "Permission denied (RBAC 'stacks:edit' missing)"
          },
          "404": {
            "description": "Git stack not found"
          },
          "500": {
            "description": "Unexpected error while updating the git stack"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "stackName": {
                    "type": "string"
                  },
                  "composePath": {
                    "type": "string"
                  },
                  "envFilePath": {
                    "type": "string"
                  },
                  "autoUpdate": {
                    "type": "boolean"
                  },
                  "autoUpdateCron": {
                    "type": "string"
                  },
                  "envVars": {
                    "type": "string"
                  },
                  "deployNow": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "autoUpdate": true,
                "autoUpdateCron": "0 3 * * *",
                "deployNow": false
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_git_stacks_id",
        "tags": [
          "git"
        ],
        "summary": "Delete a git stack (removes git files, the stack_sources record, and env var overrides)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git stack id (from GET /api/git/stacks)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (RBAC 'stacks:remove' missing)"
          },
          "404": {
            "description": "Git stack not found"
          },
          "500": {
            "description": "Unexpected error while deleting the git stack"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/git/stacks/{id}/deploy": {
      "post": {
        "operationId": "post_api_git_stacks_id_deploy",
        "tags": [
          "git"
        ],
        "summary": "Deploy a git stack, streaming the deploy log as SSE result events via the job-response channel",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git stack ID (from GET /api/git/stacks)"
          }
        ],
        "responses": {
          "200": {
            "description": "An SSE stream whose final `result` event carries {success, error}",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {}
                },
                "example": {}
              }
            }
          },
          "403": {
            "description": "Caller lacks the stacks:start permission for the stack's environment"
          },
          "404": {
            "description": "No git stack exists with that ID"
          },
          "500": {
            "description": "Failed to start the deployment"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/git/stacks/{id}/deploy-stream": {
      "post": {
        "operationId": "post_api_git_stacks_id_deploy-stream",
        "tags": [
          "git"
        ],
        "summary": "Deploy a git stack with live progress; streams SSE, or returns a jobId to poll (Accept negotiated)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git stack ID (from GET /api/git/stacks)"
          }
        ],
        "responses": {
          "200": {
            "description": "Deployment started — either a jobId to poll or a streamed SSE deploy log",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jobId": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "jobId": "a1b2c3d4"
                }
              }
            }
          },
          "403": {
            "description": "Caller lacks the stacks:start permission for the stack's environment"
          },
          "404": {
            "description": "No git stack exists with that ID"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Clients sending `Accept: application/json` get a synchronous, buffered SSE-as-JSON result; otherwise a jobId is returned immediately and progress is delivered out-of-band."
      }
    },
    "/api/git/stacks/{id}/env-files": {
      "get": {
        "operationId": "get_api_git_stacks_id_env-files",
        "tags": [
          "git"
        ],
        "summary": "List the .env files present in a git stack's cloned repository",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git stack ID (from GET /api/git/stacks)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "files": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "files"
                  ]
                },
                "example": {
                  "files": [
                    ".env",
                    ".env.prod"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The repository could not be read (e.g. not yet cloned)"
          },
          "403": {
            "description": "Caller lacks the stacks:view permission for the stack's environment"
          },
          "404": {
            "description": "No git stack exists with that ID"
          },
          "500": {
            "description": "Failed to list the env files"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Lists the `.env*` files in the git stack's synced repository checkout (read-only). Use this to discover which file to configure as the stack's `envFilePath`. This endpoint does not write — to choose the env file, PUT /api/git/stacks/{id} with `envFilePath`; to set env values, use the stack env endpoints."
      },
      "post": {
        "operationId": "post_api_git_stacks_id_env-files",
        "tags": [
          "git"
        ],
        "summary": "Read and parse one .env file from a git stack's repository into a key/value map",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git stack ID (from GET /api/git/stacks)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "vars": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "vars"
                  ]
                },
                "example": {
                  "vars": {
                    "TZ": "Europe/Berlin"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The path field is missing or the env file could not be read"
          },
          "403": {
            "description": "Caller lacks the stacks:view permission for the stack's environment"
          },
          "404": {
            "description": "No git stack exists with that ID"
          },
          "500": {
            "description": "Failed to read the env file"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Reads and parses a single `.env` file (by `path`) from the git stack's synced repository checkout into a key/value map (read-only). There is no write counterpart — git-stack env is set via `envFilePath` (PUT /api/git/stacks/{id}) or the stack env endpoints.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "path": {
                    "type": "string"
                  }
                },
                "required": [
                  "path"
                ]
              },
              "example": {
                "path": ".env.prod"
              }
            }
          }
        }
      }
    },
    "/api/git/stacks/{id}/sync": {
      "post": {
        "operationId": "post_api_git_stacks_id_sync",
        "tags": [
          "git"
        ],
        "summary": "Sync (git pull) a git stack's repository clone to the latest tracked commit",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git stack ID (from GET /api/git/stacks)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "403": {
            "description": "Caller lacks the stacks:edit permission for the stack's environment"
          },
          "404": {
            "description": "No git stack exists with that ID"
          },
          "500": {
            "description": "The sync failed"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/git/stacks/{id}/test": {
      "post": {
        "operationId": "post_api_git_stacks_id_test",
        "tags": [
          "git"
        ],
        "summary": "Test a git stack's repository access and compose configuration without deploying",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git stack ID (from GET /api/git/stacks)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "403": {
            "description": "Caller lacks the stacks:view permission for the stack's environment"
          },
          "404": {
            "description": "No git stack exists with that ID"
          },
          "500": {
            "description": "The test failed"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/git/stacks/{id}/webhook": {
      "get": {
        "operationId": "get_api_git_stacks_id_webhook",
        "tags": [
          "git"
        ],
        "summary": "GET webhook trigger for a git stack, with the secret passed as the `secret` query parameter",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git stack ID (from GET /api/git/stacks)"
          },
          {
            "name": "secret",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Webhook secret; required only if the stack has a webhook secret configured"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "skipped": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "skipped": false
                }
              }
            }
          },
          "400": {
            "description": "The id path segment is not a valid integer"
          },
          "401": {
            "description": "The provided secret did not match the stack's webhook secret"
          },
          "403": {
            "description": "Webhooks are not enabled for this stack"
          },
          "404": {
            "description": "No git stack exists with that ID"
          },
          "500": {
            "description": "The deployment triggered by the webhook failed"
          }
        },
        "security": []
      },
      "post": {
        "operationId": "post_api_git_stacks_id_webhook",
        "tags": [
          "git"
        ],
        "summary": "Webhook trigger (GitHub/GitLab) that deploys a git stack when its signature/token verifies",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git stack ID (from GET /api/git/stacks)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "skipped": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "skipped": false
                }
              }
            }
          },
          "400": {
            "description": "The id path segment is not a valid integer"
          },
          "401": {
            "description": "The webhook signature or token did not verify"
          },
          "403": {
            "description": "Webhooks are not enabled for this stack"
          },
          "404": {
            "description": "No git stack exists with that ID"
          },
          "500": {
            "description": "The deployment triggered by the webhook failed"
          }
        },
        "security": [],
        "description": "Public endpoint authenticated by the stack's webhook secret via `X-Hub-Signature-256` (GitHub) or `X-Gitlab-Token` (GitLab); the raw request body is used for HMAC verification."
      }
    },
    "/api/git/webhook/{id}": {
      "get": {
        "operationId": "get_api_git_webhook_id",
        "tags": [
          "git"
        ],
        "summary": "GET webhook trigger for a git repository, with the secret passed as the `secret` query parameter",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git repository ID (from GET /api/git/repositories)"
          },
          {
            "name": "secret",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Webhook secret; required only if the repository has a webhook secret configured"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "The id path segment is not a valid integer"
          },
          "401": {
            "description": "The provided secret did not match the repository's webhook secret"
          },
          "403": {
            "description": "Webhooks are not enabled for this repository"
          },
          "404": {
            "description": "No repository exists with that ID"
          },
          "500": {
            "description": "The deployment triggered by the webhook failed"
          }
        },
        "security": []
      },
      "post": {
        "operationId": "post_api_git_webhook_id",
        "tags": [
          "git"
        ],
        "summary": "Webhook trigger (GitHub/GitLab) that deploys from a git repository when its signature/token verifies",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Git repository ID (from GET /api/git/repositories)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "The id path segment is not a valid integer"
          },
          "401": {
            "description": "The webhook signature or token did not verify"
          },
          "403": {
            "description": "Webhooks are not enabled for this repository"
          },
          "404": {
            "description": "No repository exists with that ID"
          },
          "500": {
            "description": "The deployment triggered by the webhook failed"
          }
        },
        "security": [],
        "description": "Public endpoint authenticated by the repository's webhook secret via `X-Hub-Signature-256` (GitHub) or `X-Gitlab-Token` (GitLab); the raw request body is used for HMAC verification."
      }
    },
    "/api/hawser/connect": {
      "get": {
        "operationId": "get_api_hawser_connect",
        "tags": [
          "hawser"
        ],
        "summary": "Report the Hawser Edge WebSocket endpoint status and list currently connected agents",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "protocol": {
                      "type": "string"
                    },
                    "activeConnections": {
                      "type": "integer"
                    },
                    "connections": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "environmentId": {
                            "type": "integer"
                          },
                          "agentId": {
                            "type": "string"
                          },
                          "agentName": {
                            "type": "string"
                          },
                          "agentVersion": {
                            "type": "string"
                          },
                          "dockerVersion": {
                            "type": "string"
                          },
                          "hostname": {
                            "type": "string"
                          },
                          "capabilities": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "connectedAt": {
                            "type": "string"
                          },
                          "lastHeartbeat": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "environmentId"
                        ]
                      }
                    }
                  },
                  "required": [
                    "status",
                    "message",
                    "protocol",
                    "activeConnections",
                    "connections"
                  ]
                },
                "example": {
                  "status": "ready",
                  "message": "Hawser Edge WebSocket endpoint. Connect via WebSocket.",
                  "protocol": "wss://<host>/api/hawser/connect",
                  "activeConnections": 0,
                  "connections": []
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_hawser_connect",
        "tags": [
          "hawser"
        ],
        "summary": "Fallback for non-WebSocket clients; always advises upgrading to a WebSocket connection",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "426": {
            "description": "This endpoint requires a WebSocket upgrade (ws:// or wss://)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/hawser/tokens": {
      "get": {
        "operationId": "get_api_hawser_tokens",
        "tags": [
          "hawser"
        ],
        "summary": "List all Hawser agent tokens (only the prefix is returned, never the full token)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      },
                      "tokenPrefix": {
                        "type": "string"
                      },
                      "name": {
                        "type": "string"
                      },
                      "environmentId": {
                        "type": "integer"
                      },
                      "isActive": {
                        "type": "boolean"
                      },
                      "lastUsed": {
                        "type": "string"
                      },
                      "createdAt": {
                        "type": "string"
                      },
                      "expiresAt": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "id",
                      "tokenPrefix",
                      "name",
                      "environmentId",
                      "isActive",
                      "createdAt"
                    ]
                  }
                },
                "example": [
                  {
                    "id": 1,
                    "tokenPrefix": "hw_abc123",
                    "name": "edge-01",
                    "environmentId": 1,
                    "isActive": true,
                    "lastUsed": null,
                    "createdAt": "2026-06-01T10:00:00Z",
                    "expiresAt": null
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Not authenticated"
          },
          "403": {
            "description": "Admin access required"
          },
          "500": {
            "description": "Failed to read the tokens"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_hawser_tokens",
        "tags": [
          "hawser"
        ],
        "summary": "Generate a new Hawser agent token for an environment (the plaintext token is returned only once)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Token generated — the plaintext token is shown only once and cannot be retrieved again",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string"
                    },
                    "tokenId": {
                      "type": "integer"
                    },
                    "message": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "token",
                    "tokenId",
                    "message"
                  ]
                },
                "example": {
                  "token": "***",
                  "tokenId": 42,
                  "message": "Token generated successfully. Save this token - it will not be shown again."
                }
              }
            }
          },
          "400": {
            "description": "The name or environmentId field is missing or of the wrong type"
          },
          "401": {
            "description": "Not authenticated"
          },
          "403": {
            "description": "Admin access required"
          },
          "500": {
            "description": "Failed to generate the token"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "environmentId from GET /api/environments.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "environmentId": {
                    "type": "integer"
                  },
                  "expiresAt": {
                    "type": "string"
                  },
                  "rawToken": {
                    "type": "string"
                  }
                },
                "required": [
                  "name",
                  "environmentId"
                ]
              },
              "example": {
                "name": "edge-01",
                "environmentId": 1,
                "expiresAt": "2027-01-01T00:00:00Z"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_hawser_tokens",
        "tags": [
          "hawser"
        ],
        "summary": "Revoke a Hawser agent token by ID",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "ID of the token to revoke (from GET /api/hawser/tokens)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "message": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success",
                    "message"
                  ]
                },
                "example": {
                  "success": true,
                  "message": "Token revoked"
                }
              }
            }
          },
          "400": {
            "description": "The id query parameter is missing"
          },
          "401": {
            "description": "Not authenticated"
          },
          "403": {
            "description": "Admin access required"
          },
          "500": {
            "description": "Failed to revoke the token"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/health": {
      "get": {
        "operationId": "get_api_health",
        "tags": [
          "health"
        ],
        "summary": "Liveness probe — always returns 200 when the SvelteKit process is up",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string"
                    },
                    "timestamp": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "status",
                    "timestamp"
                  ]
                },
                "example": {
                  "status": "ok",
                  "timestamp": "2027-01-01T12:00:00.000Z"
                }
              }
            }
          }
        },
        "security": []
      }
    },
    "/api/health/database": {
      "get": {
        "operationId": "get_api_health_database",
        "tags": [
          "health"
        ],
        "summary": "Public database health check reporting schema/migration/table status; authenticated settings:view callers also get connection details",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Schema health; an unhealthy database returns the same shape with HTTP 503",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "healthy": {
                      "type": "boolean"
                    },
                    "database": {
                      "type": "string"
                    },
                    "migrationsTable": {
                      "type": "boolean"
                    },
                    "appliedMigrations": {
                      "type": "integer"
                    },
                    "pendingMigrations": {
                      "type": "integer"
                    },
                    "tables": {
                      "type": "integer"
                    },
                    "timestamp": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "healthy",
                    "database",
                    "migrationsTable",
                    "appliedMigrations",
                    "pendingMigrations",
                    "tables",
                    "timestamp"
                  ]
                },
                "example": {
                  "healthy": true,
                  "database": "sqlite",
                  "migrationsTable": true,
                  "appliedMigrations": 42,
                  "pendingMigrations": 0,
                  "tables": 25,
                  "timestamp": "2026-07-01T10:00:00.000Z"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error while checking database health"
          }
        },
        "security": [],
        "description": "Returns HTTP 200 when healthy and 503 when unhealthy (schema drift or table loss); 500 on an unexpected error. Connection details and the migration tag are only included for authenticated callers with settings:view."
      }
    },
    "/api/host": {
      "get": {
        "operationId": "get_api_host",
        "tags": [
          "host"
        ],
        "summary": "Return host info (hostname, IP, CPU, memory, uptime, Docker counts) for the daemon behind an environment",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "ID of the environment to describe (basic local info is returned when omitted) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "hostname": {
                      "type": "string"
                    },
                    "ipAddress": {
                      "type": "string"
                    },
                    "platform": {
                      "type": "string"
                    },
                    "arch": {
                      "type": "string"
                    },
                    "cpus": {
                      "type": "integer"
                    },
                    "totalMemory": {
                      "type": "integer"
                    },
                    "freeMemory": {
                      "type": "integer"
                    },
                    "uptime": {
                      "type": "integer"
                    },
                    "dockerVersion": {
                      "type": "string"
                    },
                    "dockerContainers": {
                      "type": "integer"
                    },
                    "dockerContainersRunning": {
                      "type": "integer"
                    },
                    "dockerImages": {
                      "type": "integer"
                    },
                    "environment": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "hostname",
                    "ipAddress",
                    "platform",
                    "arch",
                    "cpus",
                    "totalMemory",
                    "freeMemory",
                    "uptime",
                    "dockerContainers",
                    "dockerContainersRunning",
                    "dockerImages"
                  ]
                },
                "example": {
                  "hostname": "docker-host",
                  "ipAddress": "192.168.1.10",
                  "platform": "linux",
                  "arch": "x64",
                  "cpus": 8,
                  "totalMemory": 16777216000,
                  "freeMemory": 8388608000,
                  "uptime": 123456,
                  "dockerVersion": "27.0.3",
                  "dockerContainers": 10,
                  "dockerContainersRunning": 8,
                  "dockerImages": 25,
                  "environment": {
                    "id": 1,
                    "name": "local",
                    "connectionType": "socket"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied, or (enterprise) no access to this environment"
          },
          "500": {
            "description": "Failed to get host info"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/images": {
      "get": {
        "operationId": "get_api_images",
        "tags": [
          "images"
        ],
        "summary": "List the Docker images of an environment (returns an empty array when no env is given or Docker is unreachable)",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "ID of the environment whose images to list (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Array of images (empty if no env is specified or the Docker connection fails)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "Id": {
                        "type": "string"
                      },
                      "RepoTags": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "Size": {
                        "type": "integer"
                      },
                      "Created": {
                        "type": "integer"
                      }
                    },
                    "required": [
                      "Id"
                    ]
                  }
                },
                "example": [
                  {
                    "Id": "sha256:abc123",
                    "RepoTags": [
                      "nginx:latest"
                    ],
                    "Size": 142000000,
                    "Created": 1719830400
                  }
                ]
              }
            }
          },
          "403": {
            "description": "Permission denied, or (enterprise) no access to this environment"
          },
          "404": {
            "description": "Environment not found"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/images/{id}": {
      "delete": {
        "operationId": "delete_api_images_id",
        "tags": [
          "images"
        ],
        "summary": "Remove a Docker image by ID or name (optionally forced), scoped to an environment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Image ID or name to remove (from GET /api/images)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "ID of the environment the image belongs to (from GET /api/environments)"
          },
          {
            "name": "force",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Force removal even if the image is tagged or referenced (default false)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "403": {
            "description": "Permission denied, or (enterprise) no access to this environment"
          },
          "409": {
            "description": "Image is in use by a running container or has dependent child images"
          },
          "500": {
            "description": "Failed to remove image"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/images/{id}/export": {
      "get": {
        "operationId": "get_api_images_id_export",
        "tags": [
          "images"
        ],
        "summary": "Export a Docker image as a downloadable tar (or tar.gz) stream",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Image ID or name to export (from GET /api/images)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "ID of the environment the image belongs to (from GET /api/environments)"
          },
          {
            "name": "compress",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Gzip the tar stream and serve it as .tar.gz (default false)"
          }
        ],
        "responses": {
          "200": {
            "description": "The image tar (application/x-tar) or gzipped tar (application/gzip) as an attachment"
          },
          "403": {
            "description": "Permission denied"
          },
          "500": {
            "description": "Docker returned no response body, or the export failed"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/images/{id}/history": {
      "get": {
        "operationId": "get_api_images_id_history",
        "tags": [
          "images"
        ],
        "summary": "Return the layer build history of a Docker image",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Image ID or name whose history to return (from GET /api/images)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "ID of the environment the image belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "Id": {
                        "type": "string"
                      },
                      "Created": {
                        "type": "integer"
                      },
                      "CreatedBy": {
                        "type": "string"
                      },
                      "Size": {
                        "type": "integer"
                      },
                      "Comment": {
                        "type": "string"
                      }
                    }
                  }
                },
                "example": [
                  {
                    "Id": "sha256:abc123",
                    "Created": 1719830400,
                    "CreatedBy": "/bin/sh -c #(nop) CMD",
                    "Size": 0,
                    "Comment": ""
                  }
                ]
              }
            }
          },
          "403": {
            "description": "Permission denied"
          },
          "500": {
            "description": "Failed to get image history"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/images/{id}/tag": {
      "post": {
        "operationId": "post_api_images_id_tag",
        "tags": [
          "images"
        ],
        "summary": "Tag a Docker image into a repository (defaults the tag to \"latest\")",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Image ID or name to tag (from GET /api/images)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "ID of the environment the image belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Repository name is missing or not a string"
          },
          "403": {
            "description": "Permission denied"
          },
          "500": {
            "description": "Failed to tag image"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "repo": {
                    "type": "string"
                  },
                  "tag": {
                    "type": "string"
                  }
                },
                "required": [
                  "repo"
                ]
              },
              "example": {
                "repo": "registry.example.com/myapp",
                "tag": "v1.2.3"
              }
            }
          }
        }
      }
    },
    "/api/images/pull": {
      "post": {
        "operationId": "post_api_images_pull",
        "tags": [
          "images"
        ],
        "summary": "Pull a Docker image and stream pull (and optional scan-on-pull) progress as Server-Sent Events",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "ID of the environment to pull into (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "A Server-Sent Events stream of pull progress, ending with a \"result\" event"
          },
          "403": {
            "description": "Permission denied, or (enterprise) no access to this environment"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "image": {
                    "type": "string"
                  },
                  "scanAfterPull": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "image"
                ]
              },
              "example": {
                "image": "nginx:latest",
                "scanAfterPull": false
              }
            }
          }
        }
      }
    },
    "/api/images/push": {
      "post": {
        "operationId": "post_api_images_push",
        "tags": [
          "images"
        ],
        "summary": "Tag a local image and push it to a configured registry (streams progress, or runs synchronously for Accept: application/json)",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "ID of the environment the source image belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jobId": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "jobId": "a1b2c3d4"
                }
              }
            }
          },
          "400": {
            "description": "Image ID or registry ID missing, or the image has no usable tag"
          },
          "403": {
            "description": "Permission denied"
          },
          "404": {
            "description": "Registry not found"
          },
          "500": {
            "description": "Failed to push image"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "imageId from GET /api/images. registryId from GET /api/registries.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "imageId": {
                    "type": "string"
                  },
                  "registryId": {
                    "type": "integer"
                  },
                  "imageName": {
                    "type": "string"
                  },
                  "newTag": {
                    "type": "string"
                  }
                },
                "required": [
                  "imageId",
                  "registryId"
                ]
              },
              "example": {
                "imageId": "sha256:abc123",
                "registryId": 2,
                "imageName": "myapp:latest",
                "newTag": "myapp:v1.2.3"
              }
            }
          }
        }
      }
    },
    "/api/images/scan": {
      "get": {
        "operationId": "get_api_images_scan",
        "tags": [
          "images"
        ],
        "summary": "Return the latest cached vulnerability scan for an image, resolving its tag to a SHA256 ID",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "ID of the environment the image belongs to (from GET /api/environments)"
          },
          {
            "name": "image",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Image name or ID to look up cached results for"
          },
          {
            "name": "scanner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Restrict the lookup to a specific scanner (e.g. grype or trivy)"
          }
        ],
        "responses": {
          "200": {
            "description": "found=false when no cached scan exists; otherwise result holds the stored scan",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "found": {
                      "type": "boolean"
                    },
                    "result": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "found"
                  ]
                },
                "example": {
                  "found": true,
                  "result": {
                    "imageId": "sha256:abc123",
                    "scanner": "grype",
                    "summary": {
                      "critical": 0,
                      "high": 2
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Image name is required"
          },
          "403": {
            "description": "Permission denied"
          },
          "500": {
            "description": "Failed to get scan results"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_images_scan",
        "tags": [
          "images"
        ],
        "summary": "Start a vulnerability scan of an image and stream scan progress as Server-Sent Events, persisting the results",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "ID of the environment the image belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "A Server-Sent Events stream of scan progress, ending with a \"result\" event"
          },
          "400": {
            "description": "Image name is required"
          },
          "403": {
            "description": "Permission denied"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "imageName": {
                    "type": "string"
                  },
                  "scanner": {
                    "type": "string"
                  }
                },
                "required": [
                  "imageName"
                ]
              },
              "example": {
                "imageName": "nginx:latest",
                "scanner": "grype"
              }
            }
          }
        }
      }
    },
    "/api/images/scan/export": {
      "get": {
        "operationId": "get_api_images_scan_export",
        "tags": [
          "images"
        ],
        "summary": "Export the cached vulnerability findings for a single image as json, csv, or sarif",
        "parameters": [
          {
            "name": "imageId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Image SHA/ID to export (falls back to the \"image\" param when omitted) (from GET /api/images)"
          },
          {
            "name": "image",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Image name/ID used as a fallback when imageId is not given"
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Output format — json (default), csv, or sarif"
          }
        ],
        "responses": {
          "200": {
            "description": "A 400 is returned when imageId is missing or (auth enabled) env is not specified, and 500 on failure"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/jobs/{id}": {
      "get": {
        "operationId": "get_api_jobs_id",
        "tags": [
          "jobs"
        ],
        "summary": "Poll a background job's status and accumulated output lines (no auth — job ids are unguessable UUIDs)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Job id (UUID)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "lines": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "event": {
                            "type": "string"
                          },
                          "data": {
                            "type": "object",
                            "properties": {}
                          }
                        }
                      }
                    },
                    "result": {
                      "type": "object",
                      "properties": {}
                    }
                  },
                  "required": [
                    "id",
                    "status",
                    "lines"
                  ]
                },
                "example": {
                  "id": "string",
                  "status": "string",
                  "lines": [
                    {
                      "event": "string",
                      "data": {}
                    }
                  ],
                  "result": {}
                }
              }
            }
          },
          "404": {
            "description": "Job not found"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "delete": {
        "operationId": "delete_api_jobs_id",
        "tags": [
          "jobs"
        ],
        "summary": "Request cancellation of a running background job (no auth — job ids are unguessable UUIDs)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Job id (UUID)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "cancelled": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "cancelled"
                  ]
                },
                "example": {
                  "cancelled": true
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/labels": {
      "get": {
        "operationId": "get_api_labels",
        "tags": [
          "labels"
        ],
        "summary": "List all unique environment labels with their usage counts, the environments carrying each label, and any custom color",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "labels": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "label": {
                            "type": "string"
                          },
                          "environments": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "envId": {
                                  "type": "integer"
                                },
                                "envName": {
                                  "type": "string"
                                }
                              },
                              "required": [
                                "envId",
                                "envName"
                              ]
                            }
                          },
                          "count": {
                            "type": "integer"
                          },
                          "color": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "label",
                          "environments",
                          "count"
                        ]
                      }
                    },
                    "colors": {
                      "type": "object",
                      "properties": {}
                    }
                  },
                  "required": [
                    "labels"
                  ]
                },
                "example": {
                  "labels": [
                    {
                      "label": "prod",
                      "environments": [
                        {
                          "envId": 1,
                          "envName": "Production"
                        }
                      ],
                      "count": 1,
                      "color": "#ff0000"
                    }
                  ],
                  "colors": {
                    "prod": "#ff0000"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (requires environments:view)"
          },
          "500": {
            "description": "Failed to get labels"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_labels",
        "tags": [
          "labels"
        ],
        "summary": "Perform a bulk label operation on environments — rename, delete, add to environments, or set a custom color — selected via the action field",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "affected": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true,
                  "affected": 2
                }
              }
            }
          },
          "400": {
            "description": "Invalid action or missing/empty required fields for the chosen action"
          },
          "403": {
            "description": "Permission denied (requires environments:edit)"
          },
          "500": {
            "description": "Failed to manage labels"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "environmentIds from GET /api/environments.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string"
                  },
                  "oldLabel": {
                    "type": "string"
                  },
                  "newLabel": {
                    "type": "string"
                  },
                  "label": {
                    "type": "string"
                  },
                  "environmentIds": {
                    "type": "array",
                    "items": {
                      "type": "integer"
                    }
                  },
                  "color": {
                    "type": "string"
                  }
                },
                "required": [
                  "action"
                ]
              },
              "example": {
                "action": "rename",
                "oldLabel": "staging",
                "newLabel": "stg"
              }
            }
          }
        }
      }
    },
    "/api/legal/license": {
      "get": {
        "operationId": "get_api_legal_license",
        "tags": [
          "legal"
        ],
        "summary": "Return the bundled LICENSE.txt — as JSON by default, or as raw text/plain when format=text",
        "parameters": [
          {
            "name": "format",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Set to \"text\" to return the raw license as text/plain instead of JSON"
          }
        ],
        "responses": {
          "200": {
            "description": "The license text (as {content} JSON, or raw text/plain when format=text)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "content": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "content"
                  ]
                },
                "example": {
                  "content": "string"
                }
              }
            }
          },
          "404": {
            "description": "LICENSE.txt could not be found/read"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/legal/privacy": {
      "get": {
        "operationId": "get_api_legal_privacy",
        "tags": [
          "legal"
        ],
        "summary": "Return the bundled PRIVACY.txt — as JSON by default, or as raw text/plain when format=text",
        "parameters": [
          {
            "name": "format",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Set to \"text\" to return the raw privacy policy as text/plain instead of JSON"
          }
        ],
        "responses": {
          "200": {
            "description": "The privacy policy text (as {content} JSON, or raw text/plain when format=text)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "content": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "content"
                  ]
                },
                "example": {
                  "content": "string"
                }
              }
            }
          },
          "404": {
            "description": "PRIVACY.txt could not be found/read"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/license": {
      "get": {
        "operationId": "get_api_license",
        "tags": [
          "license"
        ],
        "summary": "Get the current license status plus the server hostname the license is bound to",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The license status object merged with the current hostname"
          },
          "401": {
            "description": "Authentication required (auth is enabled and the caller is not authenticated)"
          },
          "500": {
            "description": "Failed to read the license status"
          }
        },
        "security": []
      },
      "post": {
        "operationId": "post_api_license",
        "tags": [
          "license"
        ],
        "summary": "Activate a license by name and key",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "license": {
                      "type": "object",
                      "properties": {}
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true,
                  "license": {}
                }
              }
            }
          },
          "400": {
            "description": "Name and key are required, or activation was rejected (invalid key)"
          },
          "403": {
            "description": "Permission denied (missing license:manage)"
          },
          "500": {
            "description": "Failed to activate the license"
          }
        },
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "key": {
                    "type": "string"
                  }
                },
                "required": [
                  "name",
                  "key"
                ]
              },
              "example": {
                "name": "ACME Corp",
                "key": "***"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_license",
        "tags": [
          "license"
        ],
        "summary": "Deactivate the currently active license",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (missing license:manage)"
          },
          "500": {
            "description": "Failed to deactivate the license"
          }
        },
        "security": []
      }
    },
    "/api/logs/merged": {
      "get": {
        "operationId": "get_api_logs_merged",
        "tags": [
          "logs"
        ],
        "summary": "Stream merged, color-tagged logs from multiple containers over Server-Sent Events",
        "parameters": [
          {
            "name": "containers",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated container IDs to merge logs from"
          },
          {
            "name": "tail",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Number of lines to tail per container (default 100)"
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only return logs since this timestamp (Docker since format)"
          },
          {
            "name": "until",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only return logs until this timestamp (Docker until format)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment the containers belong to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Server-Sent Events stream (text/event-stream) emitting heartbeat and per-container log events"
          },
          "400": {
            "description": "No containers specified"
          },
          "403": {
            "description": "Permission denied (requires the containers:logs permission)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/networks": {
      "get": {
        "operationId": "get_api_networks",
        "tags": [
          "networks"
        ],
        "summary": "List Docker networks for an environment (returns an empty array when no env is given)",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID to list networks from (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "Id": {
                        "type": "string"
                      },
                      "Name": {
                        "type": "string"
                      },
                      "Driver": {
                        "type": "string"
                      },
                      "Scope": {
                        "type": "string"
                      },
                      "Internal": {
                        "type": "boolean"
                      },
                      "Attachable": {
                        "type": "boolean"
                      }
                    },
                    "required": [
                      "Id",
                      "Name"
                    ]
                  }
                },
                "example": [
                  {
                    "Id": "string",
                    "Name": "string",
                    "Driver": "string",
                    "Scope": "string",
                    "Internal": true,
                    "Attachable": true
                  }
                ]
              }
            }
          },
          "403": {
            "description": "Permission denied, or access denied to the requested environment (enterprise)"
          },
          "404": {
            "description": "Environment not found"
          },
          "500": {
            "description": "Failed to list networks"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_networks",
        "tags": [
          "networks"
        ],
        "summary": "Create a Docker network in the given environment",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID to create the network in (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "id": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success",
                    "id"
                  ]
                },
                "example": {
                  "success": true,
                  "id": "3f1c...e9"
                }
              }
            }
          },
          "400": {
            "description": "Network name is required"
          },
          "403": {
            "description": "Permission denied, or access denied to the requested environment (enterprise)"
          },
          "500": {
            "description": "Failed to create network"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "driver": {
                    "type": "string"
                  },
                  "internal": {
                    "type": "boolean"
                  },
                  "attachable": {
                    "type": "boolean"
                  },
                  "ingress": {
                    "type": "boolean"
                  },
                  "enableIPv6": {
                    "type": "boolean"
                  },
                  "options": {
                    "type": "object",
                    "properties": {}
                  },
                  "labels": {
                    "type": "object",
                    "properties": {}
                  },
                  "ipam": {
                    "type": "object",
                    "properties": {
                      "driver": {
                        "type": "string"
                      },
                      "config": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {}
                        }
                      },
                      "options": {
                        "type": "object",
                        "properties": {}
                      }
                    }
                  }
                },
                "required": [
                  "name"
                ]
              },
              "example": {
                "name": "app-net",
                "driver": "bridge",
                "attachable": true,
                "labels": {
                  "project": "dockhand"
                }
              }
            }
          }
        }
      }
    },
    "/api/networks/{id}": {
      "get": {
        "operationId": "get_api_networks_id",
        "tags": [
          "networks"
        ],
        "summary": "Inspect a Docker network by ID (a malformed ID is rejected with 400 by input validation)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Docker network ID (from GET /api/networks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment the network belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "Id": {
                      "type": "string"
                    },
                    "Name": {
                      "type": "string"
                    },
                    "Driver": {
                      "type": "string"
                    },
                    "Scope": {
                      "type": "string"
                    },
                    "IPAM": {
                      "type": "object",
                      "properties": {}
                    },
                    "Containers": {
                      "type": "object",
                      "properties": {}
                    }
                  },
                  "required": [
                    "Id",
                    "Name"
                  ]
                },
                "example": {
                  "Id": "string",
                  "Name": "string",
                  "Driver": "string",
                  "Scope": "string",
                  "IPAM": {},
                  "Containers": {}
                }
              }
            }
          },
          "403": {
            "description": "Permission denied, or access denied to the requested environment (enterprise)"
          },
          "500": {
            "description": "Failed to inspect network"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "delete": {
        "operationId": "delete_api_networks_id",
        "tags": [
          "networks"
        ],
        "summary": "Remove a Docker network by ID (a malformed ID is rejected with 400 by input validation)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Docker network ID (from GET /api/networks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment the network belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "403": {
            "description": "Permission denied, or access denied to the requested environment (enterprise)"
          },
          "500": {
            "description": "Failed to remove network"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/networks/{id}/connect": {
      "post": {
        "operationId": "post_api_networks_id_connect",
        "tags": [
          "networks"
        ],
        "summary": "Connect a container to a Docker network",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Docker network ID (from GET /api/networks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment the network belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Container ID is required"
          },
          "403": {
            "description": "Permission denied"
          },
          "500": {
            "description": "Failed to connect container to network"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "containerId from GET /api/containers.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "containerId": {
                    "type": "string"
                  },
                  "containerName": {
                    "type": "string"
                  }
                },
                "required": [
                  "containerId"
                ]
              },
              "example": {
                "containerId": "a1b2c3d4e5f6",
                "containerName": "web-1"
              }
            }
          }
        }
      }
    },
    "/api/networks/{id}/disconnect": {
      "post": {
        "operationId": "post_api_networks_id_disconnect",
        "tags": [
          "networks"
        ],
        "summary": "Disconnect a container from a Docker network (optionally forcing the disconnect)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Docker network ID (from GET /api/networks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment the network belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Container ID is required"
          },
          "403": {
            "description": "Permission denied"
          },
          "500": {
            "description": "Failed to disconnect container from network"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "containerId from GET /api/containers.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "containerId": {
                    "type": "string"
                  },
                  "containerName": {
                    "type": "string"
                  },
                  "force": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "containerId"
                ]
              },
              "example": {
                "containerId": "a1b2c3d4e5f6",
                "containerName": "web-1",
                "force": false
              }
            }
          }
        }
      }
    },
    "/api/networks/{id}/inspect": {
      "get": {
        "operationId": "get_api_networks_id_inspect",
        "tags": [
          "networks"
        ],
        "summary": "Inspect a Docker network by ID (a malformed ID is rejected with 400 by input validation)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Docker network ID (from GET /api/networks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment the network belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "Id": {
                      "type": "string"
                    },
                    "Name": {
                      "type": "string"
                    },
                    "Driver": {
                      "type": "string"
                    },
                    "Scope": {
                      "type": "string"
                    },
                    "IPAM": {
                      "type": "object",
                      "properties": {}
                    },
                    "Containers": {
                      "type": "object",
                      "properties": {}
                    }
                  },
                  "required": [
                    "Id",
                    "Name"
                  ]
                },
                "example": {
                  "Id": "string",
                  "Name": "string",
                  "Driver": "string",
                  "Scope": "string",
                  "IPAM": {},
                  "Containers": {}
                }
              }
            }
          },
          "403": {
            "description": "Permission denied"
          },
          "500": {
            "description": "Failed to inspect network"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/notifications": {
      "get": {
        "operationId": "get_api_notifications",
        "tags": [
          "notifications"
        ],
        "summary": "List all notification settings (SMTP and Apprise) with SMTP passwords masked",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      },
                      "type": {
                        "type": "string"
                      },
                      "name": {
                        "type": "string"
                      },
                      "enabled": {
                        "type": "boolean"
                      },
                      "config": {
                        "type": "object",
                        "properties": {
                          "host": {
                            "type": "string"
                          },
                          "port": {
                            "type": "integer"
                          },
                          "from_email": {
                            "type": "string"
                          },
                          "to_emails": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "urls": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "password": {
                            "type": "string"
                          }
                        }
                      },
                      "eventTypes": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    },
                    "required": [
                      "id",
                      "type",
                      "name",
                      "enabled"
                    ]
                  }
                },
                "example": [
                  {
                    "id": 0,
                    "type": "string",
                    "name": "string",
                    "enabled": true,
                    "config": {
                      "host": "string",
                      "port": 0,
                      "from_email": "string",
                      "to_emails": [
                        "string"
                      ],
                      "urls": [
                        "string"
                      ],
                      "password": "string"
                    },
                    "eventTypes": [
                      "string"
                    ]
                  }
                ]
              }
            }
          },
          "403": {
            "description": "Permission denied (requires the notifications:view permission when auth is enabled)"
          },
          "500": {
            "description": "Failed to fetch notification settings"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_notifications",
        "tags": [
          "notifications"
        ],
        "summary": "Create a notification setting (SMTP or Apprise); the response masks any SMTP password",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "type": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "config": {
                      "type": "object",
                      "properties": {}
                    },
                    "eventTypes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "id",
                    "type",
                    "name",
                    "enabled"
                  ]
                },
                "example": {
                  "id": 0,
                  "type": "string",
                  "name": "string",
                  "enabled": true,
                  "config": {},
                  "eventTypes": [
                    "string"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid fields — type/name/config required, type must be smtp or apprise, SMTP needs host/port/from_email/to_emails, Apprise needs at least one URL"
          },
          "403": {
            "description": "Permission denied (requires the notifications:create permission)"
          },
          "500": {
            "description": "Failed to create notification setting"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "enabled": {
                    "type": "boolean"
                  },
                  "config": {
                    "type": "object",
                    "properties": {
                      "host": {
                        "type": "string"
                      },
                      "port": {
                        "type": "integer"
                      },
                      "secure": {
                        "type": "boolean"
                      },
                      "username": {
                        "type": "string"
                      },
                      "password": {
                        "type": "string"
                      },
                      "from_email": {
                        "type": "string"
                      },
                      "from_name": {
                        "type": "string"
                      },
                      "to_emails": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "urls": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    }
                  },
                  "eventTypes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "event_types": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "type",
                  "name"
                ]
              },
              "example": {
                "type": "smtp",
                "name": "Ops Alerts",
                "enabled": true,
                "config": {
                  "host": "smtp.example.com",
                  "port": 587,
                  "secure": false,
                  "from_email": "dockhand@example.com",
                  "to_emails": [
                    "ops@example.com"
                  ],
                  "password": "***"
                },
                "eventTypes": [
                  "container_unhealthy"
                ]
              }
            }
          }
        }
      }
    },
    "/api/notifications/{id}": {
      "get": {
        "operationId": "get_api_notifications_id",
        "tags": [
          "notifications"
        ],
        "summary": "Get a single notification setting by ID, with any SMTP password masked",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Notification setting ID (from GET /api/notifications)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "type": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "config": {
                      "type": "object",
                      "properties": {
                        "host": {
                          "type": "string"
                        },
                        "port": {
                          "type": "integer"
                        },
                        "from_email": {
                          "type": "string"
                        },
                        "to_emails": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "urls": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "password": {
                          "type": "string"
                        }
                      }
                    },
                    "eventTypes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "id",
                    "type",
                    "name",
                    "enabled"
                  ]
                },
                "example": {
                  "id": 0,
                  "type": "string",
                  "name": "string",
                  "enabled": true,
                  "config": {
                    "host": "string",
                    "port": 0,
                    "from_email": "string",
                    "to_emails": [
                      "string"
                    ],
                    "urls": [
                      "string"
                    ],
                    "password": "string"
                  },
                  "eventTypes": [
                    "string"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid ID (not a number)"
          },
          "403": {
            "description": "Permission denied (requires the notifications:view permission)"
          },
          "404": {
            "description": "Notification setting not found"
          },
          "500": {
            "description": "Failed to fetch notification setting"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "operationId": "put_api_notifications_id",
        "tags": [
          "notifications"
        ],
        "summary": "Update a notification setting; a masked SMTP password (\"********\") keeps the stored value",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Notification setting ID (from GET /api/notifications)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "type": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "config": {
                      "type": "object",
                      "properties": {}
                    },
                    "eventTypes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "id",
                    "type",
                    "name",
                    "enabled"
                  ]
                },
                "example": {
                  "id": 0,
                  "type": "string",
                  "name": "string",
                  "enabled": true,
                  "config": {},
                  "eventTypes": [
                    "string"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid ID, or config validation failed (SMTP needs host/port/from_email/to_emails, Apprise needs at least one URL)"
          },
          "403": {
            "description": "Permission denied (requires the notifications:edit permission)"
          },
          "404": {
            "description": "Notification setting not found"
          },
          "500": {
            "description": "Failed to update notification setting"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "enabled": {
                    "type": "boolean"
                  },
                  "config": {
                    "type": "object",
                    "properties": {
                      "host": {
                        "type": "string"
                      },
                      "port": {
                        "type": "integer"
                      },
                      "secure": {
                        "type": "boolean"
                      },
                      "username": {
                        "type": "string"
                      },
                      "password": {
                        "type": "string"
                      },
                      "from_email": {
                        "type": "string"
                      },
                      "from_name": {
                        "type": "string"
                      },
                      "to_emails": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "urls": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    }
                  },
                  "eventTypes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "event_types": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "example": {
                "name": "Ops Alerts",
                "enabled": false,
                "config": {
                  "host": "smtp.example.com",
                  "port": 587,
                  "from_email": "dockhand@example.com",
                  "to_emails": [
                    "ops@example.com"
                  ],
                  "password": "********"
                },
                "eventTypes": [
                  "container_unhealthy",
                  "container_oom"
                ]
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_notifications_id",
        "tags": [
          "notifications"
        ],
        "summary": "Delete a notification setting by ID",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Notification setting ID (from GET /api/notifications)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid ID (not a number)"
          },
          "403": {
            "description": "Permission denied (requires the notifications:delete permission)"
          },
          "404": {
            "description": "Notification setting not found"
          },
          "500": {
            "description": "Failed to delete notification setting"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/notifications/{id}/test": {
      "post": {
        "operationId": "post_api_notifications_id_test",
        "tags": [
          "notifications"
        ],
        "summary": "Send a test notification through an already-saved notification setting",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Notification setting ID (from GET /api/notifications)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "message": {
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true,
                  "message": "Test notification sent successfully"
                }
              }
            }
          },
          "400": {
            "description": "Invalid ID (not a number)"
          },
          "404": {
            "description": "Notification setting not found"
          },
          "500": {
            "description": "Failed to test notification"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/notifications/test": {
      "post": {
        "operationId": "post_api_notifications_test",
        "tags": [
          "notifications"
        ],
        "summary": "Send a test notification using an ad-hoc config supplied in the body (nothing is saved)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "message": {
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true,
                  "message": "Test notification sent successfully"
                }
              }
            }
          },
          "400": {
            "description": "Missing/invalid fields — type and config required; SMTP needs host/from_email/to_emails; Apprise needs at least one URL"
          },
          "403": {
            "description": "Permission denied (requires the settings:edit permission)"
          },
          "500": {
            "description": "Failed to test notification"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "config": {
                    "type": "object",
                    "properties": {
                      "host": {
                        "type": "string"
                      },
                      "port": {
                        "type": "integer"
                      },
                      "secure": {
                        "type": "boolean"
                      },
                      "username": {
                        "type": "string"
                      },
                      "password": {
                        "type": "string"
                      },
                      "from_email": {
                        "type": "string"
                      },
                      "from_name": {
                        "type": "string"
                      },
                      "to_emails": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "urls": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "required": [
                  "type"
                ]
              },
              "example": {
                "type": "smtp",
                "name": "Test",
                "config": {
                  "host": "smtp.example.com",
                  "port": 587,
                  "from_email": "dockhand@example.com",
                  "to_emails": [
                    "ops@example.com"
                  ],
                  "password": "***"
                }
              }
            }
          }
        }
      }
    },
    "/api/notifications/trigger-test": {
      "get": {
        "operationId": "get_api_notifications_trigger-test",
        "tags": [
          "notifications"
        ],
        "summary": "List all available notification event types, grouped into categories",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "eventTypes": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "label": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "id"
                        ]
                      }
                    },
                    "categories": {
                      "type": "object",
                      "properties": {
                        "container": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "autoUpdate": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "gitStack": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "stack": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "security": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "system": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "eventTypes": [
                    {
                      "id": "string",
                      "label": "string"
                    }
                  ],
                  "categories": {
                    "container": [
                      "string"
                    ],
                    "autoUpdate": [
                      "string"
                    ],
                    "gitStack": [
                      "string"
                    ],
                    "stack": [
                      "string"
                    ],
                    "security": [
                      "string"
                    ],
                    "system": [
                      "string"
                    ]
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_notifications_trigger-test",
        "tags": [
          "notifications"
        ],
        "summary": "Trigger a real notification for a given event type (development/testing helper)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "sent": {
                      "type": "integer"
                    },
                    "eventType": {
                      "type": "string"
                    },
                    "environmentId": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "success",
                    "eventType"
                  ]
                },
                "example": {
                  "success": true,
                  "sent": 1,
                  "eventType": "container_unhealthy",
                  "environmentId": 1
                }
              }
            }
          },
          "400": {
            "description": "eventType required, payload with title and message required, unknown event type, or environmentId missing for a non-system event"
          },
          "500": {
            "description": "Unknown error while sending the notification"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "environmentId from GET /api/environments.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "eventType": {
                    "type": "string"
                  },
                  "environmentId": {
                    "type": "integer"
                  },
                  "payload": {
                    "type": "object",
                    "properties": {
                      "title": {
                        "type": "string"
                      },
                      "message": {
                        "type": "string"
                      },
                      "type": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "title",
                      "message"
                    ]
                  }
                },
                "required": [
                  "eventType"
                ]
              },
              "example": {
                "eventType": "container_unhealthy",
                "environmentId": 1,
                "payload": {
                  "title": "Container unhealthy",
                  "message": "web-1 is unhealthy",
                  "type": "warning"
                }
              }
            }
          }
        }
      }
    },
    "/api/preferences/favorite-groups": {
      "get": {
        "operationId": "get_api_preferences_favorite-groups",
        "tags": [
          "preferences"
        ],
        "summary": "Get the saved log favorite-groups for an environment",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "groups": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "containers": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        },
                        "required": [
                          "name"
                        ]
                      }
                    }
                  }
                },
                "example": {
                  "groups": [
                    {
                      "name": "frontend",
                      "containers": [
                        "web-1",
                        "web-2"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Environment ID is required, or invalid (not a number)"
          },
          "500": {
            "description": "Failed to get favorite groups"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_preferences_favorite-groups",
        "tags": [
          "preferences"
        ],
        "summary": "Add, remove, update or reorder log favorite-groups for an environment",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "groups": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "containers": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        },
                        "required": [
                          "name"
                        ]
                      }
                    }
                  }
                },
                "example": {
                  "groups": [
                    {
                      "name": "string",
                      "containers": [
                        "string"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid environmentId, unknown action, missing group name, empty/missing containers, duplicate group name, or missing groups array for reorder"
          },
          "404": {
            "description": "Group not found (update action)"
          },
          "500": {
            "description": "Failed to update favorite groups"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "environmentId from GET /api/environments.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "environmentId": {
                    "type": "integer"
                  },
                  "action": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "containers": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "newName": {
                    "type": "string"
                  },
                  "groups": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "containers": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      },
                      "required": [
                        "name"
                      ]
                    }
                  }
                },
                "required": [
                  "environmentId",
                  "action"
                ]
              },
              "example": {
                "environmentId": 1,
                "action": "add",
                "name": "frontend",
                "containers": [
                  "web-1",
                  "web-2"
                ]
              }
            }
          }
        }
      }
    },
    "/api/preferences/favorites": {
      "get": {
        "operationId": "get_api_preferences_favorites",
        "tags": [
          "preferences"
        ],
        "summary": "Get the saved log favorites (container names) for an environment",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "favorites": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                },
                "example": {
                  "favorites": [
                    "web-1",
                    "db-1"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Environment ID is required, or invalid (not a number)"
          },
          "500": {
            "description": "Failed to get favorites"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_preferences_favorites",
        "tags": [
          "preferences"
        ],
        "summary": "Add, remove or reorder log favorites (container names) for an environment",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "favorites": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                },
                "example": {
                  "favorites": [
                    "string"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid environmentId, unknown action, missing favorites array for reorder, or missing containerName for add/remove"
          },
          "500": {
            "description": "Failed to update favorites"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "environmentId from GET /api/environments.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "environmentId": {
                    "type": "integer"
                  },
                  "action": {
                    "type": "string"
                  },
                  "containerName": {
                    "type": "string"
                  },
                  "favorites": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "environmentId",
                  "action"
                ]
              },
              "example": {
                "environmentId": 1,
                "action": "add",
                "containerName": "web-1"
              }
            }
          }
        }
      }
    },
    "/api/preferences/grid": {
      "get": {
        "operationId": "get_api_preferences_grid",
        "tags": [
          "preferences"
        ],
        "summary": "Retrieve all saved data-grid column preferences (per-user when auth is enabled)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "preferences": {
                      "type": "object",
                      "properties": {}
                    }
                  }
                },
                "example": {
                  "preferences": {
                    "containers": {
                      "columns": [
                        {
                          "id": "name",
                          "visible": true
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Failed to get grid preferences"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_preferences_grid",
        "tags": [
          "preferences"
        ],
        "summary": "Save column preferences for one data grid and return all grid preferences",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "preferences": {
                      "type": "object",
                      "properties": {}
                    }
                  }
                },
                "example": {
                  "preferences": {}
                }
              }
            }
          },
          "400": {
            "description": "gridId is required, columns array is required, or a column is missing id (string) / visible (boolean)"
          },
          "500": {
            "description": "Failed to save grid preferences"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "gridId": {
                    "type": "string"
                  },
                  "columns": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "visible": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "id",
                        "visible"
                      ]
                    }
                  }
                },
                "required": [
                  "gridId"
                ]
              },
              "example": {
                "gridId": "containers",
                "columns": [
                  {
                    "id": "name",
                    "visible": true
                  },
                  {
                    "id": "image",
                    "visible": false
                  }
                ]
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_preferences_grid",
        "tags": [
          "preferences"
        ],
        "summary": "Reset grid preferences — a single grid when gridId is given, otherwise all grids",
        "parameters": [
          {
            "name": "gridId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Grid ID to reset; omit to reset every grid"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "preferences": {
                      "type": "object",
                      "properties": {}
                    }
                  }
                },
                "example": {
                  "preferences": {}
                }
              }
            }
          },
          "500": {
            "description": "Failed to reset grid preferences"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/preferences/sidebar": {
      "get": {
        "operationId": "get_api_preferences_sidebar",
        "tags": [
          "preferences"
        ],
        "summary": "Retrieve the saved sidebar menu preferences (order and hidden items)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "preferences": {
                      "type": "object",
                      "properties": {
                        "order": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "hidden": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "preferences": {
                    "order": [
                      "dashboard",
                      "containers"
                    ],
                    "hidden": [
                      "volumes"
                    ]
                  }
                }
              }
            }
          },
          "500": {
            "description": "Failed to get sidebar preferences"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_preferences_sidebar",
        "tags": [
          "preferences"
        ],
        "summary": "Save the sidebar menu preferences (order and hidden items)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "preferences": {
                      "type": "object",
                      "properties": {
                        "order": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "hidden": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "preferences": {
                    "order": [
                      "string"
                    ],
                    "hidden": [
                      "string"
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "description": "order must be an array of strings, or hidden must be an array of strings"
          },
          "500": {
            "description": "Failed to save sidebar preferences"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "order": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "hidden": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "order",
                  "hidden"
                ]
              },
              "example": {
                "order": [
                  "dashboard",
                  "containers",
                  "stacks"
                ],
                "hidden": [
                  "volumes"
                ]
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_preferences_sidebar",
        "tags": [
          "preferences"
        ],
        "summary": "Reset the sidebar menu preferences to their default order/visibility",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "preferences": {
                      "type": "object",
                      "properties": {
                        "order": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "hidden": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "preferences": {
                    "order": [
                      "string"
                    ],
                    "hidden": [
                      "string"
                    ]
                  }
                }
              }
            }
          },
          "500": {
            "description": "Failed to reset sidebar preferences"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/profile": {
      "get": {
        "operationId": "get_api_profile",
        "tags": [
          "profile"
        ],
        "summary": "Get the authenticated user's own profile",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "username": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string"
                    },
                    "displayName": {
                      "type": "string"
                    },
                    "avatar": {
                      "type": "string"
                    },
                    "mfaEnabled": {
                      "type": "boolean"
                    },
                    "isAdmin": {
                      "type": "boolean"
                    },
                    "provider": {
                      "type": "string"
                    },
                    "lastLogin": {
                      "type": "string"
                    },
                    "createdAt": {
                      "type": "string"
                    },
                    "updatedAt": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "username",
                    "mfaEnabled",
                    "isAdmin",
                    "provider",
                    "createdAt",
                    "updatedAt"
                  ]
                },
                "example": {
                  "id": 0,
                  "username": "string",
                  "email": "string",
                  "displayName": "string",
                  "avatar": "string",
                  "mfaEnabled": true,
                  "isAdmin": true,
                  "provider": "string",
                  "lastLogin": "string",
                  "createdAt": "string",
                  "updatedAt": "string"
                }
              }
            }
          },
          "400": {
            "description": "Authentication is not enabled"
          },
          "401": {
            "description": "Not authenticated"
          },
          "404": {
            "description": "User not found"
          },
          "500": {
            "description": "Failed to read the profile"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "operationId": "put_api_profile",
        "tags": [
          "profile"
        ],
        "summary": "Update the authenticated user's own profile (email/display name, and optionally the password with current-password confirmation)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "username": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string"
                    },
                    "displayName": {
                      "type": "string"
                    },
                    "avatar": {
                      "type": "string"
                    },
                    "mfaEnabled": {
                      "type": "boolean"
                    },
                    "isAdmin": {
                      "type": "boolean"
                    },
                    "lastLogin": {
                      "type": "string"
                    },
                    "createdAt": {
                      "type": "string"
                    },
                    "updatedAt": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "username",
                    "mfaEnabled",
                    "isAdmin",
                    "createdAt",
                    "updatedAt"
                  ]
                },
                "example": {
                  "id": 0,
                  "username": "string",
                  "email": "string",
                  "displayName": "string",
                  "avatar": "string",
                  "mfaEnabled": true,
                  "isAdmin": true,
                  "lastLogin": "string",
                  "createdAt": "string",
                  "updatedAt": "string"
                }
              }
            }
          },
          "400": {
            "description": "Authentication is not enabled, current password missing, new password too short (<8), or current password incorrect"
          },
          "401": {
            "description": "Not authenticated"
          },
          "404": {
            "description": "User not found"
          },
          "500": {
            "description": "Failed to update the profile"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string"
                  },
                  "displayName": {
                    "type": "string"
                  },
                  "currentPassword": {
                    "type": "string"
                  },
                  "newPassword": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "displayName": "Jane Doe",
                "currentPassword": "***",
                "newPassword": "***"
              }
            }
          }
        }
      }
    },
    "/api/profile/avatar": {
      "post": {
        "operationId": "post_api_profile_avatar",
        "tags": [
          "profile"
        ],
        "summary": "Upload the authenticated user's avatar as a base64 image data URL (max ~500KB)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "avatar": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success",
                    "avatar"
                  ]
                },
                "example": {
                  "success": true,
                  "avatar": "string"
                }
              }
            }
          },
          "400": {
            "description": "Authentication is not enabled, avatar data missing, not an image data URL, or image too large (>500KB)"
          },
          "401": {
            "description": "Not authenticated"
          },
          "500": {
            "description": "Failed to upload the avatar"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "avatar": {
                    "type": "string"
                  }
                },
                "required": [
                  "avatar"
                ]
              },
              "example": {
                "avatar": "data:image/png;base64,iVBORw0KGgo..."
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_profile_avatar",
        "tags": [
          "profile"
        ],
        "summary": "Remove the authenticated user's avatar",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Authentication is not enabled"
          },
          "401": {
            "description": "Not authenticated"
          },
          "500": {
            "description": "Failed to remove the avatar"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/profile/preferences": {
      "get": {
        "operationId": "get_api_profile_preferences",
        "tags": [
          "profile"
        ],
        "summary": "Get the authenticated user's theme preferences",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "lightTheme": {
                      "type": "string"
                    },
                    "darkTheme": {
                      "type": "string"
                    },
                    "font": {
                      "type": "string"
                    },
                    "fontSize": {
                      "type": "string"
                    },
                    "gridFontSize": {
                      "type": "string"
                    },
                    "terminalFont": {
                      "type": "string"
                    },
                    "editorFont": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "lightTheme": "string",
                  "darkTheme": "string",
                  "font": "string",
                  "fontSize": "string",
                  "gridFontSize": "string",
                  "terminalFont": "string",
                  "editorFont": "string"
                }
              }
            }
          },
          "400": {
            "description": "Authentication is not enabled"
          },
          "401": {
            "description": "Not authenticated"
          },
          "500": {
            "description": "Failed to read the preferences"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "operationId": "put_api_profile_preferences",
        "tags": [
          "profile"
        ],
        "summary": "Update the authenticated user's theme preferences (each supplied value is validated against the allowed theme/font/size lists)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "lightTheme": {
                      "type": "string"
                    },
                    "darkTheme": {
                      "type": "string"
                    },
                    "font": {
                      "type": "string"
                    },
                    "fontSize": {
                      "type": "string"
                    },
                    "gridFontSize": {
                      "type": "string"
                    },
                    "terminalFont": {
                      "type": "string"
                    },
                    "editorFont": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "lightTheme": "string",
                  "darkTheme": "string",
                  "font": "string",
                  "fontSize": "string",
                  "gridFontSize": "string",
                  "terminalFont": "string",
                  "editorFont": "string"
                }
              }
            }
          },
          "400": {
            "description": "Authentication is not enabled, or a supplied theme/font/size value is invalid"
          },
          "401": {
            "description": "Not authenticated"
          },
          "500": {
            "description": "Failed to update the preferences"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "lightTheme": {
                    "type": "string"
                  },
                  "darkTheme": {
                    "type": "string"
                  },
                  "font": {
                    "type": "string"
                  },
                  "fontSize": {
                    "type": "string"
                  },
                  "gridFontSize": {
                    "type": "string"
                  },
                  "terminalFont": {
                    "type": "string"
                  },
                  "editorFont": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "darkTheme": "tokyo-night",
                "font": "inter",
                "fontSize": "normal"
              }
            }
          }
        }
      }
    },
    "/api/prune/all": {
      "post": {
        "operationId": "post_api_prune_all",
        "tags": [
          "prune"
        ],
        "summary": "Prune all unused Docker resources (containers, images, volumes and networks) in a single operation",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Target environment id; scopes both the prune operation and the permission check (defaults to the local environment) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Returns { success: true, result } where result is the aggregated Docker prune report (space reclaimed, items deleted)"
          },
          "403": {
            "description": "Permission denied — requires the \"remove\" permission on containers, images, volumes AND networks for the target environment"
          },
          "500": {
            "description": "Failed to prune the system (Docker error); the message is returned in \"details\""
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/prune/containers": {
      "post": {
        "operationId": "post_api_prune_containers",
        "tags": [
          "prune"
        ],
        "summary": "Prune (delete) all stopped containers in the target environment",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Target environment id; scopes both the prune operation and the permission check (defaults to the local environment) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Returns { success: true, result } where result is the Docker container-prune report (deleted container ids, space reclaimed)"
          },
          "403": {
            "description": "Permission denied — requires the \"remove\" permission on containers for the target environment"
          },
          "500": {
            "description": "Failed to prune containers (Docker error)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/prune/images": {
      "post": {
        "operationId": "post_api_prune_images",
        "tags": [
          "prune"
        ],
        "summary": "Prune unused Docker images, streaming progress as a Server-Sent Events job",
        "parameters": [
          {
            "name": "dangling",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "When not \"false\", prune only dangling images; set \"dangling=false\" to prune all unused images (defaults to dangling-only)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Target environment id; scopes both the prune operation and the permission check (defaults to the local environment) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Server-Sent Events stream; the final `result` event contains { success, result } or { success:false, error }"
          },
          "403": {
            "description": "Permission denied — requires the \"remove\" permission on images for the target environment"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Returns a text/event-stream. On completion a `result` event carries { success, result } on success or { success:false, error } on failure — the operation itself never returns a non-200 HTTP status once the permission check passes."
      }
    },
    "/api/prune/networks": {
      "post": {
        "operationId": "post_api_prune_networks",
        "tags": [
          "prune"
        ],
        "summary": "Prune (delete) all unused Docker networks in the target environment",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Target environment id; scopes both the prune operation and the permission check (defaults to the local environment) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Returns { success: true, result } where result is the Docker network-prune report (deleted network names)"
          },
          "403": {
            "description": "Permission denied — requires the \"remove\" permission on networks for the target environment"
          },
          "500": {
            "description": "Failed to prune networks (Docker error)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/prune/volumes": {
      "post": {
        "operationId": "post_api_prune_volumes",
        "tags": [
          "prune"
        ],
        "summary": "Prune (delete) all unused Docker volumes in the target environment",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Target environment id; scopes both the prune operation and the permission check (defaults to the local environment) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Returns { success: true, result } where result is the Docker volume-prune report (deleted volume names, space reclaimed)"
          },
          "403": {
            "description": "Permission denied — requires the \"remove\" permission on volumes for the target environment"
          },
          "500": {
            "description": "Failed to prune volumes (Docker error)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/registries": {
      "get": {
        "operationId": "get_api_registries",
        "tags": [
          "registries"
        ],
        "summary": "List all configured container registries with passwords stripped (only a hasCredentials flag)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      },
                      "name": {
                        "type": "string"
                      },
                      "url": {
                        "type": "string"
                      },
                      "isDefault": {
                        "type": "boolean"
                      },
                      "hasCredentials": {
                        "type": "boolean"
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "url",
                      "hasCredentials"
                    ]
                  }
                },
                "example": [
                  {
                    "id": 1,
                    "name": "Docker Hub",
                    "url": "https://docker.io",
                    "isDefault": true,
                    "hasCredentials": false
                  }
                ]
              }
            }
          },
          "403": {
            "description": "Caller lacks the registries:view permission"
          },
          "500": {
            "description": "Failed to read registries"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_registries",
        "tags": [
          "registries"
        ],
        "summary": "Create a container registry (optionally set it as default); credentials are trimmed and stored encrypted",
        "parameters": [],
        "responses": {
          "201": {
            "description": "Registry created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "isDefault": {
                      "type": "boolean"
                    },
                    "hasCredentials": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "url",
                    "hasCredentials"
                  ]
                },
                "example": {
                  "id": 0,
                  "name": "string",
                  "url": "string",
                  "isDefault": true,
                  "hasCredentials": true
                }
              }
            }
          },
          "400": {
            "description": "Missing name or url, or a duplicate registry name"
          },
          "403": {
            "description": "Caller lacks the registries:create permission"
          },
          "500": {
            "description": "Failed to create the registry"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "url": {
                    "type": "string"
                  },
                  "username": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  },
                  "isDefault": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "name",
                  "url"
                ]
              },
              "example": {
                "name": "GHCR",
                "url": "https://ghcr.io",
                "username": "deploy",
                "password": "***",
                "isDefault": false
              }
            }
          }
        }
      }
    },
    "/api/registries/{id}": {
      "get": {
        "operationId": "get_api_registries_id",
        "tags": [
          "registries"
        ],
        "summary": "Get a single registry by ID with the password stripped (only a hasCredentials flag)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Registry ID (from GET /api/registries)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "isDefault": {
                      "type": "boolean"
                    },
                    "hasCredentials": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "url",
                    "hasCredentials"
                  ]
                },
                "example": {
                  "id": 0,
                  "name": "string",
                  "url": "string",
                  "isDefault": true,
                  "hasCredentials": true
                }
              }
            }
          },
          "400": {
            "description": "The id path segment is not a valid integer"
          },
          "403": {
            "description": "Caller lacks the registries:view permission"
          },
          "404": {
            "description": "No registry exists with that ID"
          },
          "500": {
            "description": "Failed to read the registry"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "operationId": "put_api_registries_id",
        "tags": [
          "registries"
        ],
        "summary": "Update a registry (optionally set as default); credentials are trimmed and the response strips the password",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Registry ID (from GET /api/registries)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "isDefault": {
                      "type": "boolean"
                    },
                    "hasCredentials": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "url",
                    "hasCredentials"
                  ]
                },
                "example": {
                  "id": 0,
                  "name": "string",
                  "url": "string",
                  "isDefault": true,
                  "hasCredentials": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid id, or a duplicate registry name"
          },
          "403": {
            "description": "Caller lacks the registries:edit permission"
          },
          "404": {
            "description": "No registry exists with that ID"
          },
          "500": {
            "description": "The update failed"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "url": {
                    "type": "string"
                  },
                  "username": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  },
                  "isDefault": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "name": "GHCR",
                "url": "https://ghcr.io",
                "username": "deploy",
                "password": "***",
                "isDefault": true
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_registries_id",
        "tags": [
          "registries"
        ],
        "summary": "Delete a registry by ID",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Registry ID (from GET /api/registries)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid id, or the registry cannot be deleted"
          },
          "403": {
            "description": "Caller lacks the registries:delete permission"
          },
          "404": {
            "description": "No registry exists with that ID"
          },
          "500": {
            "description": "The deletion failed"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/registries/{id}/default": {
      "post": {
        "operationId": "post_api_registries_id_default",
        "tags": [
          "registries"
        ],
        "summary": "Mark a registry as the default registry",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Registry ID (from GET /api/registries)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "The id path segment is not a valid integer"
          },
          "403": {
            "description": "Caller lacks the settings:edit permission"
          },
          "404": {
            "description": "No registry exists with that ID"
          },
          "500": {
            "description": "Failed to set the default registry"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/registries/test": {
      "post": {
        "operationId": "post_api_registries_test",
        "tags": [
          "registries"
        ],
        "summary": "Test registry connectivity and (if credentials are given) authentication against the V2 endpoint",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "connectivity": {
                      "type": "boolean"
                    },
                    "authenticated": {
                      "type": "boolean"
                    },
                    "message": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success",
                    "connectivity",
                    "message"
                  ]
                },
                "example": {
                  "success": true,
                  "connectivity": true,
                  "authenticated": true,
                  "message": "Connected and authenticated as deploy"
                }
              }
            }
          },
          "400": {
            "description": "The url field is missing (and no registryId was supplied)"
          },
          "403": {
            "description": "Caller lacks the registries:view permission"
          },
          "404": {
            "description": "The referenced registryId does not exist"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Pass either a saved registryId or inline url/username/password. The outcome is always reported in a 200 body via the success/connectivity/authenticated fields, never as an HTTP error. registryId from GET /api/registries.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "registryId": {
                    "type": "integer"
                  },
                  "url": {
                    "type": "string"
                  },
                  "username": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "url": "https://ghcr.io",
                "username": "deploy",
                "password": "***"
              }
            }
          }
        }
      }
    },
    "/api/registry/catalog": {
      "get": {
        "operationId": "get_api_registry_catalog",
        "tags": [
          "registry"
        ],
        "summary": "List repositories in a registry's V2 catalog (with Harbor project-API fallback), paginated",
        "parameters": [
          {
            "name": "registry",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "ID of the configured registry to query (from GET /api/registries)"
          },
          {
            "name": "last",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Opaque pagination cursor from a previous page's nextLast"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "repositories": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          },
                          "star_count": {
                            "type": "integer"
                          },
                          "is_official": {
                            "type": "boolean"
                          },
                          "is_automated": {
                            "type": "boolean"
                          }
                        },
                        "required": [
                          "name"
                        ]
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "pageSize": {
                          "type": "integer"
                        },
                        "hasMore": {
                          "type": "boolean"
                        },
                        "nextLast": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "pageSize",
                        "hasMore"
                      ]
                    }
                  },
                  "required": [
                    "repositories",
                    "pagination"
                  ]
                },
                "example": {
                  "repositories": [
                    {
                      "name": "library/nginx",
                      "description": "",
                      "star_count": 0,
                      "is_official": false,
                      "is_automated": false
                    }
                  ],
                  "pagination": {
                    "pageSize": 100,
                    "hasMore": false,
                    "nextLast": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing registry parameter, or Docker Hub was targeted (catalog listing unsupported)"
          },
          "404": {
            "description": "Registry not found, or the registry does not implement the V2 catalog API"
          },
          "500": {
            "description": "Failed to fetch the catalog"
          },
          "503": {
            "description": "Could not connect to the registry (connection refused, host not found, or a TLS error)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Docker Hub is rejected (no catalog API). For 401/403/404 from the upstream registry the same status is proxied back to the caller."
      }
    },
    "/api/registry/image": {
      "delete": {
        "operationId": "delete_api_registry_image",
        "tags": [
          "registry"
        ],
        "summary": "Delete a tagged image from a registry by resolving its manifest digest, then deleting by digest",
        "parameters": [
          {
            "name": "registry",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "ID of the configured registry (from GET /api/registries)"
          },
          {
            "name": "image",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Repository/image name (may include an org path)"
          },
          {
            "name": "tag",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Tag to delete"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "message": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true,
                  "message": "Deleted library/nginx:1.27"
                }
              }
            }
          },
          "400": {
            "description": "A required query parameter is missing, or Docker Hub was targeted (deletion unsupported)"
          },
          "401": {
            "description": "The registry rejected authentication while resolving or deleting the manifest"
          },
          "403": {
            "description": "Caller lacks the settings:edit permission"
          },
          "404": {
            "description": "Registry not found, or the image/tag/manifest does not exist"
          },
          "405": {
            "description": "The registry has manifest deletion disabled (REGISTRY_STORAGE_DELETE_ENABLED)"
          },
          "500": {
            "description": "Failed to delete the image"
          },
          "503": {
            "description": "Could not connect to the registry (connection refused or host not found)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Docker Hub deletion is rejected. Upstream 401 responses during manifest resolution/deletion are proxied back as 401."
      }
    },
    "/api/registry/search": {
      "get": {
        "operationId": "get_api_registry_search",
        "tags": [
          "registry"
        ],
        "summary": "Search for images by name in Docker Hub or a configured private registry (direct + catalog fallback)",
        "parameters": [
          {
            "name": "term",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Search term / image name"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Maximum number of results (default 25)"
          },
          {
            "name": "registry",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "ID of the configured registry to search; omit to search Docker Hub (from GET /api/registries)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string"
                      },
                      "description": {
                        "type": "string"
                      },
                      "star_count": {
                        "type": "integer"
                      },
                      "is_official": {
                        "type": "boolean"
                      },
                      "is_automated": {
                        "type": "boolean"
                      }
                    },
                    "required": [
                      "name"
                    ]
                  }
                },
                "example": [
                  {
                    "name": "nginx",
                    "description": "Official build of Nginx",
                    "star_count": 20000,
                    "is_official": true,
                    "is_automated": false
                  }
                ]
              }
            }
          },
          "400": {
            "description": "The term query parameter is missing"
          },
          "404": {
            "description": "The referenced registry does not exist"
          },
          "500": {
            "description": "Failed to search images"
          },
          "503": {
            "description": "Could not connect to the registry (connection refused or host not found)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "With no registry parameter the search runs against Docker Hub; otherwise against the given registry, falling back to Docker Hub search when the registry is a Docker Hub mirror."
      }
    },
    "/api/registry/tag-info": {
      "get": {
        "operationId": "get_api_registry_tag-info",
        "tags": [
          "registry"
        ],
        "summary": "Resolve the compressed size and creation date of a single registry tag from its manifest (lazy per-tag fetch for the registry browser)",
        "parameters": [
          {
            "name": "registry",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Registry ID; omitted or Docker Hub registries return a graceful \"not supported\" result instead of a manifest fetch (from GET /api/registries)"
          },
          {
            "name": "image",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Image repository/name"
          },
          {
            "name": "tag",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Tag name or digest"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "size": {
                      "type": "number"
                    },
                    "lastUpdated": {
                      "type": "string"
                    },
                    "reason": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "size": 104857600,
                  "lastUpdated": "2026-06-01T12:00:00Z"
                }
              }
            }
          },
          "400": {
            "description": "image and tag are required"
          },
          "404": {
            "description": "Registry not found"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/registry/tags": {
      "get": {
        "operationId": "get_api_registry_tags",
        "tags": [
          "registry"
        ],
        "summary": "List the tags of an image from Docker Hub or a configured registry (paginated for Docker Hub)",
        "parameters": [
          {
            "name": "registry",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "ID of the configured registry; omit to query Docker Hub (from GET /api/registries)"
          },
          {
            "name": "image",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Repository/image name"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Page number for Docker Hub pagination (default 1)"
          },
          {
            "name": "pageSize",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Page size for Docker Hub pagination (default 20)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tags": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "size": {
                            "type": "integer"
                          },
                          "lastUpdated": {
                            "type": "string"
                          },
                          "digest": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "name"
                        ]
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "pageSize": {
                      "type": "integer"
                    },
                    "hasNext": {
                      "type": "boolean"
                    },
                    "hasPrev": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "tags",
                    "total",
                    "page",
                    "pageSize",
                    "hasNext",
                    "hasPrev"
                  ]
                },
                "example": {
                  "tags": [
                    {
                      "name": "1.27",
                      "lastUpdated": "2026-06-01T00:00:00Z"
                    }
                  ],
                  "total": 1,
                  "page": 1,
                  "pageSize": 20,
                  "hasNext": false,
                  "hasPrev": false
                }
              }
            }
          },
          "400": {
            "description": "The image query parameter is missing"
          },
          "404": {
            "description": "The referenced registry does not exist"
          },
          "500": {
            "description": "Failed to fetch tags"
          },
          "503": {
            "description": "Could not connect to the registry (connection refused or host not found)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "With no registry parameter, Docker Hub is queried; V2 registries return all tags in one page. Upstream Docker Hub error status codes are proxied back."
      }
    },
    "/api/roles": {
      "get": {
        "operationId": "get_api_roles",
        "tags": [
          "roles"
        ],
        "summary": "List all roles (built-in and custom); available in setup mode or with an enterprise license",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      },
                      "name": {
                        "type": "string"
                      },
                      "description": {
                        "type": "string"
                      },
                      "isSystem": {
                        "type": "boolean"
                      },
                      "permissions": {
                        "type": "object",
                        "properties": {}
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "isSystem"
                    ]
                  }
                },
                "example": [
                  {
                    "id": 0,
                    "name": "string",
                    "description": "string",
                    "isSystem": true,
                    "permissions": {}
                  }
                ]
              }
            }
          },
          "403": {
            "description": "Enterprise license required"
          },
          "500": {
            "description": "Failed to read the roles"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_roles",
        "tags": [
          "roles"
        ],
        "summary": "Create a custom role (enterprise; admin required when auth is enabled)",
        "parameters": [],
        "responses": {
          "201": {
            "description": "The created role"
          },
          "400": {
            "description": "Name and permissions are required"
          },
          "403": {
            "description": "Enterprise license required, or admin access required"
          },
          "409": {
            "description": "A role with this name already exists"
          },
          "500": {
            "description": "Failed to create the role"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "environmentIds from GET /api/environments.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "permissions": {
                    "type": "object",
                    "properties": {}
                  },
                  "environmentIds": {
                    "type": "array",
                    "items": {
                      "type": "integer"
                    }
                  }
                },
                "required": [
                  "name",
                  "permissions"
                ]
              },
              "example": {
                "name": "Operators",
                "description": "Can manage containers",
                "permissions": {
                  "containers": [
                    "view",
                    "edit"
                  ]
                },
                "environmentIds": [
                  1,
                  2
                ]
              }
            }
          }
        }
      }
    },
    "/api/roles/{id}": {
      "get": {
        "operationId": "get_api_roles_id",
        "tags": [
          "roles"
        ],
        "summary": "Get a single role by id; available in setup mode or with an enterprise license",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric id of the role (from GET /api/roles)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    },
                    "isSystem": {
                      "type": "boolean"
                    },
                    "permissions": {
                      "type": "object",
                      "properties": {}
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "isSystem"
                  ]
                },
                "example": {
                  "id": 0,
                  "name": "string",
                  "description": "string",
                  "isSystem": true,
                  "permissions": {}
                }
              }
            }
          },
          "400": {
            "description": "Role id is required"
          },
          "403": {
            "description": "Enterprise license required"
          },
          "404": {
            "description": "Role not found"
          },
          "500": {
            "description": "Failed to read the role"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "operationId": "put_api_roles_id",
        "tags": [
          "roles"
        ],
        "summary": "Update a custom role (system roles cannot be modified; enterprise, admin required when auth is enabled)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric id of the role (from GET /api/roles)"
          }
        ],
        "responses": {
          "200": {
            "description": "The updated role"
          },
          "400": {
            "description": "Role id is required, or the role is a system role and cannot be modified"
          },
          "403": {
            "description": "Enterprise license required, or admin access required"
          },
          "404": {
            "description": "Role not found"
          },
          "409": {
            "description": "A role with this name already exists"
          },
          "500": {
            "description": "Failed to update the role"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "environmentIds from GET /api/environments.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "permissions": {
                    "type": "object",
                    "properties": {}
                  },
                  "environmentIds": {
                    "type": "array",
                    "items": {
                      "type": "integer"
                    }
                  }
                }
              },
              "example": {
                "description": "Updated description",
                "permissions": {
                  "containers": [
                    "view"
                  ]
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_roles_id",
        "tags": [
          "roles"
        ],
        "summary": "Delete a custom role by id (system roles cannot be deleted; enterprise, admin required when auth is enabled)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric id of the role (from GET /api/roles)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Role id is required, or the role is a system role and cannot be deleted"
          },
          "403": {
            "description": "Enterprise license required, or admin access required"
          },
          "404": {
            "description": "Role not found"
          },
          "500": {
            "description": "Failed to delete the role"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/schedules": {
      "get": {
        "operationId": "get_api_schedules",
        "tags": [
          "schedules"
        ],
        "summary": "List all schedules (container/env auto-updates, git syncs, image-prune, backups, repo maintenance, system jobs)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "schedules": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "type": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "entityName": {
                            "type": "string"
                          },
                          "environmentId": {
                            "type": "integer"
                          },
                          "enabled": {
                            "type": "boolean"
                          },
                          "cronExpression": {
                            "type": "string"
                          },
                          "nextRun": {
                            "type": "string"
                          },
                          "isSystem": {
                            "type": "boolean"
                          }
                        },
                        "required": [
                          "id",
                          "type",
                          "name",
                          "entityName",
                          "enabled",
                          "isSystem"
                        ]
                      }
                    }
                  },
                  "required": [
                    "schedules"
                  ]
                },
                "example": {
                  "schedules": [
                    {
                      "id": 0,
                      "type": "string",
                      "name": "string",
                      "entityName": "string",
                      "environmentId": 0,
                      "enabled": true,
                      "cronExpression": "string",
                      "nextRun": "string",
                      "isSystem": true
                    }
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error while assembling the schedule list"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/schedules/{type}/{id}": {
      "delete": {
        "operationId": "delete_api_schedules_type_id",
        "tags": [
          "schedules"
        ],
        "summary": "Delete (or disable, for policy-driven types) a schedule by type and id",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Schedule type (container_update, git_stack_sync, env_update_check, image_prune, backup, repo_prune, repo_check, repo_verify, system_cleanup)"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Schedule id (semantics depend on type — container/git-stack/backup-config id, environment id, or a synthetic repo-policy id) (from GET /api/schedules)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid schedule id, invalid/unsupported type, or system_cleanup (cannot be removed)"
          },
          "404": {
            "description": "Schedule (or backup destination, for repo_* types) not found"
          },
          "500": {
            "description": "Unexpected error while deleting the schedule"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/schedules/{type}/{id}/run": {
      "post": {
        "operationId": "post_api_schedules_type_id_run",
        "tags": [
          "schedules"
        ],
        "summary": "Manually trigger a single run of a schedule (outside its cron), by type and id",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Schedule type (container_update, git_stack_sync, system_cleanup, env_update_check, image_prune, backup, repo_prune, repo_check, repo_verify)"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Schedule id (semantics depend on type) (from GET /api/schedules)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "message": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success",
                    "message"
                  ]
                },
                "example": {
                  "success": true,
                  "message": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid schedule id/type, or the triggered task itself reported failure"
          },
          "404": {
            "description": "Schedule, backup config, or backup destination not found (or backup feature disabled)"
          },
          "500": {
            "description": "Unexpected error while triggering the schedule"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/schedules/{type}/{id}/toggle": {
      "post": {
        "operationId": "post_api_schedules_type_id_toggle",
        "tags": [
          "schedules"
        ],
        "summary": "Toggle a schedule's enabled/disabled state (registers/unregisters the croner job accordingly)",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Schedule type (container_update, git_stack_sync, env_update_check, image_prune, backup, repo_prune, repo_check, repo_verify, system_cleanup)"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Schedule id (semantics depend on type) (from GET /api/schedules)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "enabled": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success",
                    "enabled"
                  ]
                },
                "example": {
                  "success": true,
                  "enabled": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid schedule id, unsupported type, or system_cleanup (cannot be paused)"
          },
          "404": {
            "description": "Schedule, backup config, or backup destination not found"
          },
          "500": {
            "description": "Unexpected error while toggling the schedule"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/schedules/executions": {
      "get": {
        "operationId": "get_api_schedules_executions",
        "tags": [
          "schedules"
        ],
        "summary": "List schedule execution history, filterable and paginated",
        "parameters": [
          {
            "name": "scheduleType",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter by schedule type (container_update, git_stack_sync, ...)"
          },
          {
            "name": "scheduleId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Filter by the numeric id of the schedule (from GET /api/schedules)"
          },
          {
            "name": "environmentId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Filter by environment id (\"null\" for global/system schedules) (from GET /api/environments)"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter by execution status (queued/running/success/warning/failed/skipped)"
          },
          {
            "name": "statuses",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated list of statuses (alternative to status)"
          },
          {
            "name": "triggeredBy",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter by trigger (cron/webhook/manual)"
          },
          {
            "name": "fromDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "ISO date lower bound"
          },
          {
            "name": "toDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "ISO date upper bound"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Page size (default 50)"
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Page offset (default 0)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "executions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "scheduleType": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "startedAt": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "id",
                          "scheduleType",
                          "status",
                          "startedAt"
                        ]
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "executions",
                    "total",
                    "limit",
                    "offset"
                  ]
                },
                "example": {
                  "executions": [
                    {
                      "id": 0,
                      "scheduleType": "string",
                      "status": "string",
                      "startedAt": "string"
                    }
                  ],
                  "total": 0,
                  "limit": 0,
                  "offset": 0
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error while loading execution history"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/schedules/executions/{id}": {
      "get": {
        "operationId": "get_api_schedules_executions_id",
        "tags": [
          "schedules"
        ],
        "summary": "Get a single schedule execution, including its logs",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Execution id (from GET /api/schedules/executions)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "scheduleType": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "startedAt": {
                      "type": "string"
                    },
                    "log": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "scheduleType",
                    "status",
                    "startedAt"
                  ]
                },
                "example": {
                  "id": 0,
                  "scheduleType": "string",
                  "status": "string",
                  "startedAt": "string",
                  "log": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid execution id"
          },
          "404": {
            "description": "Execution not found"
          },
          "500": {
            "description": "Unexpected error while loading the execution"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "delete": {
        "operationId": "delete_api_schedules_executions_id",
        "tags": [
          "schedules"
        ],
        "summary": "Delete a single schedule execution record",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Execution id (from GET /api/schedules/executions)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid execution id"
          },
          "403": {
            "description": "Permission denied (RBAC 'schedules:edit' missing)"
          },
          "500": {
            "description": "Unexpected error while deleting the execution"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/schedules/settings": {
      "get": {
        "operationId": "get_api_schedules_settings",
        "tags": [
          "schedules"
        ],
        "summary": "Get the schedules-page display preference (hide system jobs), per-user with a global fallback",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "hideSystemJobs": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "hideSystemJobs"
                  ]
                },
                "example": {
                  "hideSystemJobs": true
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error while loading the setting"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "operationId": "put_api_schedules_settings",
        "tags": [
          "schedules"
        ],
        "summary": "Update the schedules-page display preference (hide system jobs) for the current user",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "hideSystemJobs": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true,
                  "hideSystemJobs": true
                }
              }
            }
          },
          "400": {
            "description": "hideSystemJobs is not a boolean"
          },
          "500": {
            "description": "Unexpected error while saving the setting"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "hideSystemJobs": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "hideSystemJobs": true
              }
            }
          }
        }
      }
    },
    "/api/schedules/stream": {
      "get": {
        "operationId": "get_api_schedules_stream",
        "tags": [
          "schedules"
        ],
        "summary": "Server-Sent Events stream that polls and pushes schedule updates (same shape as GET /api/schedules)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "text/event-stream response, periodically emitting the current schedule list as SSE \"data:\" messages"
          },
          "403": {
            "description": "Permission denied (RBAC 'schedules:view' missing)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/schedules/system/{id}/toggle": {
      "post": {
        "operationId": "post_api_schedules_system_id_toggle",
        "tags": [
          "schedules"
        ],
        "summary": "Toggle one of the three built-in system cleanup jobs (schedule/event/scanner cleanup) on or off",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "System schedule id (1=schedule cleanup, 2=event cleanup, 4=scanner cleanup) (from GET /api/schedules)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "enabled": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success",
                    "enabled"
                  ]
                },
                "example": {
                  "success": true,
                  "enabled": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid or unknown system schedule id"
          },
          "403": {
            "description": "Permission denied (RBAC 'settings:edit' missing)"
          },
          "500": {
            "description": "Unexpected error while toggling the job"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/self-update": {
      "post": {
        "operationId": "post_api_self-update",
        "tags": [
          "self-update"
        ],
        "summary": "Pull a new Dockhand image and hand off to an updater sidecar that replaces this running container",
        "parameters": [],
        "responses": {
          "200": {
            "description": "text/event-stream SSE response (steps: pulling_image, building_config, pulling_updater, creating_container, launching_updater, then a \"launched\"/\"error\" event) — or, with \"Accept: application/json\", the final event as plain JSON"
          },
          "400": {
            "description": "newImage missing, not running in Docker, Docker socket is read-only, or own container has no name"
          },
          "403": {
            "description": "Admin access required"
          },
          "500": {
            "description": "Failed to inspect own container or determine its name before starting the update"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "newImage": {
                    "type": "string"
                  }
                },
                "required": [
                  "newImage"
                ]
              },
              "example": {
                "newImage": "fnsys/dockhand:v1.0.40"
              }
            }
          }
        }
      }
    },
    "/api/self-update/check": {
      "get": {
        "operationId": "get_api_self-update_check",
        "tags": [
          "self-update"
        ],
        "summary": "Check whether a newer Dockhand image is available (version-tag compare via GitHub changelog, or registry-digest compare for mutable tags like :latest)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "updateAvailable": {
                      "type": "boolean"
                    },
                    "currentImage": {
                      "type": "string"
                    },
                    "newImage": {
                      "type": "string"
                    },
                    "latestVersion": {
                      "type": "string"
                    },
                    "currentDigest": {
                      "type": "string"
                    },
                    "newDigest": {
                      "type": "string"
                    },
                    "containerName": {
                      "type": "string"
                    },
                    "isComposeManaged": {
                      "type": "boolean"
                    },
                    "isLocalImage": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "updateAvailable"
                  ]
                },
                "example": {
                  "updateAvailable": true,
                  "currentImage": "string",
                  "newImage": "string",
                  "latestVersion": "string",
                  "currentDigest": "string",
                  "newDigest": "string",
                  "containerName": "string",
                  "isComposeManaged": true,
                  "isLocalImage": true,
                  "error": "string"
                }
              }
            }
          },
          "403": {
            "description": "Admin access required"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/self-update/progress": {
      "get": {
        "operationId": "get_api_self-update_progress",
        "tags": [
          "self-update"
        ],
        "summary": "Poll the self-update sidecar container's logs and exit state for progress tracking",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Updater container id (from POST /api/self-update)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "logs": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "exitCode": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "logs",
                    "status"
                  ]
                },
                "example": {
                  "logs": "string",
                  "status": "string",
                  "exitCode": 0
                }
              }
            }
          },
          "400": {
            "description": "Container ID is required"
          },
          "403": {
            "description": "Admin access required"
          },
          "500": {
            "description": "Failed to inspect the updater container, or failed to fetch its progress"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/settings/general": {
      "get": {
        "operationId": "get_api_settings_general",
        "tags": [
          "settings"
        ],
        "summary": "Get the global general settings (UI preferences, retention/cleanup schedules, monitoring intervals, theme defaults, stack paths)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "confirmDestructive": {
                      "type": "boolean"
                    },
                    "showStoppedContainers": {
                      "type": "boolean"
                    },
                    "highlightUpdates": {
                      "type": "boolean"
                    },
                    "timeFormat": {
                      "type": "string"
                    },
                    "dateFormat": {
                      "type": "string"
                    },
                    "downloadFormat": {
                      "type": "string"
                    },
                    "defaultGrypeArgs": {
                      "type": "string"
                    },
                    "defaultTrivyArgs": {
                      "type": "string"
                    },
                    "scheduleRetentionDays": {
                      "type": "integer"
                    },
                    "eventRetentionDays": {
                      "type": "integer"
                    },
                    "scheduleCleanupCron": {
                      "type": "string"
                    },
                    "eventCleanupCron": {
                      "type": "string"
                    },
                    "scheduleCleanupEnabled": {
                      "type": "boolean"
                    },
                    "eventCleanupEnabled": {
                      "type": "boolean"
                    },
                    "logBufferSizeKb": {
                      "type": "integer"
                    },
                    "defaultTimezone": {
                      "type": "string"
                    },
                    "eventCollectionMode": {
                      "type": "string"
                    },
                    "eventPollInterval": {
                      "type": "integer"
                    },
                    "metricsCollectionInterval": {
                      "type": "integer"
                    },
                    "lightTheme": {
                      "type": "string"
                    },
                    "darkTheme": {
                      "type": "string"
                    },
                    "font": {
                      "type": "string"
                    },
                    "fontSize": {
                      "type": "string"
                    },
                    "gridFontSize": {
                      "type": "string"
                    },
                    "terminalFont": {
                      "type": "string"
                    },
                    "editorFont": {
                      "type": "string"
                    },
                    "externalStackPaths": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "primaryStackLocation": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "confirmDestructive",
                    "showStoppedContainers",
                    "highlightUpdates",
                    "timeFormat",
                    "dateFormat",
                    "downloadFormat",
                    "defaultGrypeArgs",
                    "defaultTrivyArgs",
                    "scheduleRetentionDays",
                    "eventRetentionDays",
                    "scheduleCleanupCron",
                    "eventCleanupCron",
                    "scheduleCleanupEnabled",
                    "eventCleanupEnabled",
                    "logBufferSizeKb",
                    "defaultTimezone",
                    "eventCollectionMode",
                    "eventPollInterval",
                    "metricsCollectionInterval",
                    "lightTheme",
                    "darkTheme",
                    "font",
                    "fontSize",
                    "gridFontSize",
                    "terminalFont",
                    "editorFont"
                  ]
                },
                "example": {
                  "confirmDestructive": true,
                  "showStoppedContainers": true,
                  "highlightUpdates": true,
                  "timeFormat": "string",
                  "dateFormat": "string",
                  "downloadFormat": "string",
                  "defaultGrypeArgs": "string",
                  "defaultTrivyArgs": "string",
                  "scheduleRetentionDays": 0,
                  "eventRetentionDays": 0,
                  "scheduleCleanupCron": "string",
                  "eventCleanupCron": "string",
                  "scheduleCleanupEnabled": true,
                  "eventCleanupEnabled": true,
                  "logBufferSizeKb": 0,
                  "defaultTimezone": "string",
                  "eventCollectionMode": "string",
                  "eventPollInterval": 0,
                  "metricsCollectionInterval": 0,
                  "lightTheme": "string",
                  "darkTheme": "string",
                  "font": "string",
                  "fontSize": "string",
                  "gridFontSize": "string",
                  "terminalFont": "string",
                  "editorFont": "string",
                  "externalStackPaths": [
                    "string"
                  ],
                  "primaryStackLocation": "string"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required (auth is enabled and the caller is not authenticated)"
          },
          "500": {
            "description": "Failed to read the general settings"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_settings_general",
        "tags": [
          "settings"
        ],
        "summary": "Update the global general settings (only supplied, valid fields are persisted; returns the full effective settings)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "confirmDestructive": {
                      "type": "boolean"
                    },
                    "showStoppedContainers": {
                      "type": "boolean"
                    },
                    "highlightUpdates": {
                      "type": "boolean"
                    },
                    "timeFormat": {
                      "type": "string"
                    },
                    "dateFormat": {
                      "type": "string"
                    },
                    "downloadFormat": {
                      "type": "string"
                    },
                    "defaultGrypeArgs": {
                      "type": "string"
                    },
                    "defaultTrivyArgs": {
                      "type": "string"
                    },
                    "scheduleRetentionDays": {
                      "type": "integer"
                    },
                    "eventRetentionDays": {
                      "type": "integer"
                    },
                    "scheduleCleanupCron": {
                      "type": "string"
                    },
                    "eventCleanupCron": {
                      "type": "string"
                    },
                    "scheduleCleanupEnabled": {
                      "type": "boolean"
                    },
                    "eventCleanupEnabled": {
                      "type": "boolean"
                    },
                    "logBufferSizeKb": {
                      "type": "integer"
                    },
                    "defaultTimezone": {
                      "type": "string"
                    },
                    "eventCollectionMode": {
                      "type": "string"
                    },
                    "eventPollInterval": {
                      "type": "integer"
                    },
                    "metricsCollectionInterval": {
                      "type": "integer"
                    },
                    "lightTheme": {
                      "type": "string"
                    },
                    "darkTheme": {
                      "type": "string"
                    },
                    "font": {
                      "type": "string"
                    },
                    "fontSize": {
                      "type": "string"
                    },
                    "gridFontSize": {
                      "type": "string"
                    },
                    "terminalFont": {
                      "type": "string"
                    },
                    "editorFont": {
                      "type": "string"
                    },
                    "externalStackPaths": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "primaryStackLocation": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "confirmDestructive",
                    "showStoppedContainers",
                    "highlightUpdates",
                    "timeFormat",
                    "dateFormat",
                    "downloadFormat",
                    "defaultGrypeArgs",
                    "defaultTrivyArgs",
                    "scheduleRetentionDays",
                    "eventRetentionDays",
                    "scheduleCleanupCron",
                    "eventCleanupCron",
                    "scheduleCleanupEnabled",
                    "eventCleanupEnabled",
                    "logBufferSizeKb",
                    "defaultTimezone",
                    "eventCollectionMode",
                    "eventPollInterval",
                    "metricsCollectionInterval",
                    "lightTheme",
                    "darkTheme",
                    "font",
                    "fontSize",
                    "gridFontSize",
                    "terminalFont",
                    "editorFont"
                  ]
                },
                "example": {
                  "confirmDestructive": true,
                  "showStoppedContainers": true,
                  "highlightUpdates": true,
                  "timeFormat": "string",
                  "dateFormat": "string",
                  "downloadFormat": "string",
                  "defaultGrypeArgs": "string",
                  "defaultTrivyArgs": "string",
                  "scheduleRetentionDays": 0,
                  "eventRetentionDays": 0,
                  "scheduleCleanupCron": "string",
                  "eventCleanupCron": "string",
                  "scheduleCleanupEnabled": true,
                  "eventCleanupEnabled": true,
                  "logBufferSizeKb": 0,
                  "defaultTimezone": "string",
                  "eventCollectionMode": "string",
                  "eventPollInterval": 0,
                  "metricsCollectionInterval": 0,
                  "lightTheme": "string",
                  "darkTheme": "string",
                  "font": "string",
                  "fontSize": "string",
                  "gridFontSize": "string",
                  "terminalFont": "string",
                  "editorFont": "string",
                  "externalStackPaths": [
                    "string"
                  ],
                  "primaryStackLocation": "string"
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (missing settings:edit)"
          },
          "500": {
            "description": "Failed to save the general settings"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "confirmDestructive": {
                    "type": "boolean"
                  },
                  "showStoppedContainers": {
                    "type": "boolean"
                  },
                  "highlightUpdates": {
                    "type": "boolean"
                  },
                  "timeFormat": {
                    "type": "string"
                  },
                  "dateFormat": {
                    "type": "string"
                  },
                  "downloadFormat": {
                    "type": "string"
                  },
                  "defaultGrypeArgs": {
                    "type": "string"
                  },
                  "defaultTrivyArgs": {
                    "type": "string"
                  },
                  "scheduleRetentionDays": {
                    "type": "integer"
                  },
                  "eventRetentionDays": {
                    "type": "integer"
                  },
                  "scheduleCleanupCron": {
                    "type": "string"
                  },
                  "eventCleanupCron": {
                    "type": "string"
                  },
                  "scheduleCleanupEnabled": {
                    "type": "boolean"
                  },
                  "eventCleanupEnabled": {
                    "type": "boolean"
                  },
                  "logBufferSizeKb": {
                    "type": "integer"
                  },
                  "defaultTimezone": {
                    "type": "string"
                  },
                  "eventCollectionMode": {
                    "type": "string"
                  },
                  "eventPollInterval": {
                    "type": "integer"
                  },
                  "metricsCollectionInterval": {
                    "type": "integer"
                  },
                  "lightTheme": {
                    "type": "string"
                  },
                  "darkTheme": {
                    "type": "string"
                  },
                  "font": {
                    "type": "string"
                  },
                  "fontSize": {
                    "type": "string"
                  },
                  "gridFontSize": {
                    "type": "string"
                  },
                  "terminalFont": {
                    "type": "string"
                  },
                  "editorFont": {
                    "type": "string"
                  },
                  "externalStackPaths": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "primaryStackLocation": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "confirmDestructive": true,
                "timeFormat": "24h",
                "dateFormat": "DD.MM.YYYY",
                "eventCollectionMode": "stream",
                "metricsCollectionInterval": 30000
              }
            }
          }
        }
      }
    },
    "/api/settings/navigation": {
      "get": {
        "operationId": "get_api_settings_navigation",
        "tags": [
          "settings"
        ],
        "summary": "Get the effective navigation preferences (landing page, env-click target) plus the raw global and per-user values",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "effective": {
                      "type": "object",
                      "properties": {
                        "landingPage": {
                          "type": "string"
                        },
                        "envClickPage": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "landingPage",
                        "envClickPage"
                      ]
                    },
                    "global": {
                      "type": "object",
                      "properties": {
                        "landingPage": {
                          "type": "string"
                        },
                        "envClickPage": {
                          "type": "string"
                        }
                      }
                    },
                    "user": {
                      "type": "object",
                      "properties": {
                        "landingPage": {
                          "type": "string"
                        },
                        "envClickPage": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "effective": {
                    "landingPage": "dashboard",
                    "envClickPage": "containers"
                  },
                  "global": {
                    "landingPage": null,
                    "envClickPage": null
                  },
                  "user": null
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "operationId": "put_api_settings_navigation",
        "tags": [
          "settings"
        ],
        "summary": "Update navigation preferences (landing page, env-click target) at global or per-user scope",
        "parameters": [
          {
            "name": "scope",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Preference scope to update — \"global\" (default) or \"user\""
          }
        ],
        "responses": {
          "200": {
            "description": "The updated preferences ({success:true, global:{...}} for scope=global, or {success:true, user:{...}} for scope=user)"
          },
          "400": {
            "description": "Invalid input — invalid landingPage/envClickPage value, or scope=user requested while authentication is disabled"
          },
          "401": {
            "description": "Not authenticated (scope=user, authentication is enabled but no valid session)"
          },
          "403": {
            "description": "Permission denied (scope=global, requires settings:edit)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "landingPage": {
                    "type": "string"
                  },
                  "envClickPage": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "landingPage": "containers",
                "envClickPage": "logs"
              }
            }
          }
        }
      }
    },
    "/api/settings/scanner": {
      "get": {
        "operationId": "get_api_settings_scanner",
        "tags": [
          "settings"
        ],
        "summary": "Get the vulnerability-scanner settings for an environment, plus (unless settingsOnly) scanner availability, versions and optional update info",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id to read scanner settings for (falls back to global defaults) (from GET /api/environments)"
          },
          {
            "name": "checkUpdates",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "When true, also check the scanner images for available updates (slower)"
          },
          {
            "name": "settingsOnly",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "When true, return only settings + defaults and skip the Docker availability/version checks"
          }
        ],
        "responses": {
          "200": {
            "description": "Scanner settings and (unless settingsOnly) availability, versions, updates and defaults"
          },
          "403": {
            "description": "Permission denied (missing settings:view for the environment)"
          },
          "500": {
            "description": "Failed to read the scanner settings"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_settings_scanner",
        "tags": [
          "settings"
        ],
        "summary": "Save the vulnerability-scanner settings for an environment",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "settings": {
                      "type": "object",
                      "properties": {
                        "scanner": {
                          "type": "string"
                        },
                        "grypeArgs": {
                          "type": "string"
                        },
                        "trivyArgs": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "scanner",
                        "grypeArgs",
                        "trivyArgs"
                      ]
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true,
                  "settings": {
                    "scanner": "string",
                    "grypeArgs": "string",
                    "trivyArgs": "string"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid scanner type (must be none, grype, trivy or both)"
          },
          "403": {
            "description": "Permission denied (missing settings:edit for the environment)"
          },
          "500": {
            "description": "Failed to save the scanner settings"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "scanner": {
                    "type": "string"
                  },
                  "grypeArgs": {
                    "type": "string"
                  },
                  "trivyArgs": {
                    "type": "string"
                  },
                  "envId": {
                    "type": "integer"
                  }
                }
              },
              "example": {
                "scanner": "grype",
                "grypeArgs": "-o json -v {image}",
                "trivyArgs": "image --format json {image}",
                "envId": 1
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_settings_scanner",
        "tags": [
          "settings"
        ],
        "summary": "Remove the scanner images (grype/trivy) and clean up scanner database volumes for an environment",
        "parameters": [
          {
            "name": "removeImages",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Must be true to actually perform the removal (required)"
          },
          {
            "name": "scanner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Which scanner image to remove (grype or trivy); omit to remove both"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id whose scanner images should be removed (required) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "removed": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true,
                  "removed": [
                    "string"
                  ],
                  "errors": [
                    "string"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The removeImages parameter is required, or the environment id is missing"
          },
          "403": {
            "description": "Permission denied (missing settings:edit for the environment)"
          },
          "404": {
            "description": "Environment not found"
          },
          "500": {
            "description": "Failed to remove the scanner images"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/settings/scanner/cache": {
      "delete": {
        "operationId": "delete_api_settings_scanner_cache",
        "tags": [
          "settings"
        ],
        "summary": "Clear the vulnerability-scanner cache (local volumes/bind-mount dirs plus each remote environment's scanner volume)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "removedVolumes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "removedDirs": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "skippedEnvironments": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "success",
                    "removedVolumes",
                    "removedDirs",
                    "skippedEnvironments"
                  ]
                },
                "example": {
                  "success": true,
                  "removedVolumes": [
                    "string"
                  ],
                  "removedDirs": [
                    "string"
                  ],
                  "skippedEnvironments": [
                    "string"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Permission denied"
          },
          "500": {
            "description": "Failed to clear scanner cache"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/settings/theme": {
      "get": {
        "operationId": "get_api_settings_theme",
        "tags": [
          "settings"
        ],
        "summary": "Get the app-level theme settings (public — used by the login page before authentication); defaults are returned on error",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "lightTheme": {
                      "type": "string"
                    },
                    "darkTheme": {
                      "type": "string"
                    },
                    "font": {
                      "type": "string"
                    },
                    "fontSize": {
                      "type": "string"
                    },
                    "gridFontSize": {
                      "type": "string"
                    },
                    "terminalFont": {
                      "type": "string"
                    },
                    "editorFont": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "lightTheme",
                    "darkTheme",
                    "font",
                    "fontSize",
                    "gridFontSize",
                    "terminalFont",
                    "editorFont"
                  ]
                },
                "example": {
                  "lightTheme": "default",
                  "darkTheme": "default",
                  "font": "system",
                  "fontSize": "normal",
                  "gridFontSize": "normal",
                  "terminalFont": "system-mono",
                  "editorFont": "system-mono"
                }
              }
            }
          }
        },
        "security": []
      }
    },
    "/api/stacks": {
      "get": {
        "operationId": "get_api_stacks",
        "tags": [
          "stacks"
        ],
        "summary": "List compose stacks (running + database-only entries) for one environment",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id — an empty array is returned if omitted (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string"
                      },
                      "containers": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "status": {
                        "type": "string"
                      },
                      "sourceType": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "name",
                      "containers",
                      "status"
                    ]
                  }
                },
                "example": [
                  {
                    "name": "immich",
                    "containers": [
                      "immich_server",
                      "immich_redis"
                    ],
                    "status": "running",
                    "sourceType": "git"
                  }
                ]
              }
            }
          },
          "403": {
            "description": "Permission denied, or access denied to this environment (RBAC)"
          },
          "404": {
            "description": "Environment not found"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_stacks",
        "tags": [
          "stacks"
        ],
        "summary": "Create an internal (non-git) stack; optionally deploy immediately",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id to create the stack in (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "started": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success",
                    "started"
                  ]
                },
                "example": {
                  "success": true,
                  "started": true
                }
              }
            }
          },
          "400": {
            "description": "Missing/invalid name or compose content"
          },
          "403": {
            "description": "Permission denied (RBAC 'stacks:create' missing)"
          },
          "500": {
            "description": "Deploy failed (docker compose error) — stack record may still have been created"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "compose": {
                    "type": "string"
                  },
                  "start": {
                    "type": "boolean"
                  },
                  "envVars": {
                    "type": "string"
                  },
                  "rawEnvContent": {
                    "type": "string"
                  },
                  "composePath": {
                    "type": "string"
                  },
                  "envPath": {
                    "type": "string"
                  }
                },
                "required": [
                  "name",
                  "compose"
                ]
              },
              "example": {
                "name": "gitcheck",
                "compose": "services:\n  web:\n    image: nginx:alpine\n",
                "start": true
              }
            }
          }
        }
      }
    },
    "/api/stacks/{name}": {
      "delete": {
        "operationId": "delete_api_stacks_name",
        "tags": [
          "stacks"
        ],
        "summary": "Remove a stack completely (compose down + delete files + database cleanup)",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stack name (from GET /api/stacks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          },
          {
            "name": "force",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Force removal even if the compose down step fails"
          },
          {
            "name": "volumes",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Also remove named volumes (docker compose down --volumes)"
          },
          {
            "name": "files",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Delete the stack's on-disk files/directory too (default true; pass files=false to keep them on disk)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Compose down failed and force was not set"
          },
          "403": {
            "description": "Permission denied, or access denied to this environment"
          },
          "404": {
            "description": "Compose file not found for this stack"
          },
          "500": {
            "description": "Unexpected error while removing the stack"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/stacks/{name}/check-path-change": {
      "post": {
        "operationId": "post_api_stacks_name_check-path-change",
        "tags": [
          "stacks"
        ],
        "summary": "Check whether a proposed compose path moves the stack to a different directory and how many files the old directory still holds",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stack name (from GET /api/stacks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the stack belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "hasChanges": {
                      "type": "boolean"
                    },
                    "oldDir": {
                      "type": "string"
                    },
                    "newDir": {
                      "type": "string"
                    },
                    "fileCount": {
                      "type": "integer"
                    },
                    "currentComposePath": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "hasChanges",
                    "fileCount"
                  ]
                },
                "example": {
                  "hasChanges": true,
                  "oldDir": "/opt/stacks/old",
                  "newDir": "/opt/stacks/web",
                  "fileCount": 3,
                  "currentComposePath": "/opt/stacks/old/compose.yaml"
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (requires stacks:edit)"
          },
          "500": {
            "description": "Failed to check path changes"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "newComposePath": {
                    "type": "string"
                  }
                },
                "required": [
                  "newComposePath"
                ]
              },
              "example": {
                "newComposePath": "/opt/stacks/web/compose.yaml"
              }
            }
          }
        }
      }
    },
    "/api/stacks/{name}/compose": {
      "get": {
        "operationId": "get_api_stacks_name_compose",
        "tags": [
          "stacks"
        ],
        "summary": "Read the resolved compose file content for a stack, along with the resolved compose/env file paths",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stack name (from GET /api/stacks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the stack belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "content": {
                      "type": "string"
                    },
                    "stackDir": {
                      "type": "string"
                    },
                    "composePath": {
                      "type": "string"
                    },
                    "envPath": {
                      "type": "string"
                    },
                    "suggestedEnvPath": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "content"
                  ]
                },
                "example": {
                  "content": "services:\n  web:\n    image: nginx",
                  "stackDir": "/opt/stacks/web",
                  "composePath": "/opt/stacks/web/compose.yaml",
                  "envPath": "/opt/stacks/web/.env",
                  "suggestedEnvPath": "/opt/stacks/web/.env"
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (requires stacks:view)"
          },
          "404": {
            "description": "Compose file not found — the response carries needsFileLocation plus the suggested composePath/envPath"
          },
          "500": {
            "description": "Failed to get compose file"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "operationId": "put_api_stacks_name_compose",
        "tags": [
          "stacks"
        ],
        "summary": "Save the compose file content (with optional custom/moved paths); when restart=true the stack is redeployed with force-recreate and progress streams over SSE",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stack name (from GET /api/stacks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the stack belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "File saved (restart=false) or a Server-Sent-Events job stream (restart=true) reporting deploy progress and the final result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Compose file content is required"
          },
          "403": {
            "description": "Permission denied (requires stacks:edit)"
          },
          "500": {
            "description": "Failed to save or deploy the compose file"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string"
                  },
                  "restart": {
                    "type": "boolean"
                  },
                  "composePath": {
                    "type": "string"
                  },
                  "envPath": {
                    "type": "string"
                  },
                  "moveFromDir": {
                    "type": "string"
                  },
                  "oldComposePath": {
                    "type": "string"
                  },
                  "oldEnvPath": {
                    "type": "string"
                  }
                },
                "required": [
                  "content"
                ]
              },
              "example": {
                "content": "services:\n  web:\n    image: nginx",
                "restart": false
              }
            }
          }
        }
      }
    },
    "/api/stacks/{name}/delete-preview": {
      "get": {
        "operationId": "get_api_stacks_name_delete-preview",
        "tags": [
          "stacks"
        ],
        "summary": "Preview the on-disk directories and named volumes a delete-with-files would remove for a stack",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stack name (from GET /api/stacks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the stack belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "stackName": {
                      "type": "string"
                    },
                    "sourceType": {
                      "type": "string"
                    },
                    "stackDir": {
                      "type": "string"
                    },
                    "gitDir": {
                      "type": "string"
                    },
                    "namedVolumes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "canDeleteFiles": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "stackName",
                    "namedVolumes",
                    "canDeleteFiles"
                  ]
                },
                "example": {
                  "stackName": "web",
                  "sourceType": "internal",
                  "stackDir": "/opt/stacks/web",
                  "gitDir": null,
                  "namedVolumes": [
                    "web_data"
                  ],
                  "canDeleteFiles": true
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (requires stacks:remove)"
          },
          "500": {
            "description": "Failed to compute the deletion preview"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/stacks/{name}/deploy": {
      "post": {
        "operationId": "post_api_stacks_name_deploy",
        "tags": [
          "stacks"
        ],
        "summary": "Deploy (docker compose up) a stack, optionally pulling images, building, and force-recreating; progress and the final result stream over Server-Sent Events",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stack name (from GET /api/stacks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the stack belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Server-Sent-Events job stream with progress events and a final result event ({success, output})"
          },
          "403": {
            "description": "Permission denied (requires stacks:start, or environment access denied on enterprise)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "pull": {
                    "type": "boolean"
                  },
                  "build": {
                    "type": "boolean"
                  },
                  "forceRecreate": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "pull": true,
                "build": false,
                "forceRecreate": false
              }
            }
          }
        }
      }
    },
    "/api/stacks/{name}/down": {
      "post": {
        "operationId": "post_api_stacks_name_down",
        "tags": [
          "stacks"
        ],
        "summary": "Take a stack down (docker compose down — removes containers, keeps files), asynchronously",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stack name (from GET /api/stacks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Fire-and-forget job id — poll GET /api/jobs/{jobId} for the result. Send \"Accept: application/json\" (without text/event-stream) to instead block and receive the final {success,output|error} synchronously.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jobId": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "jobId"
                  ]
                },
                "example": {
                  "jobId": "3f9c5b1a-2e4d-4a6f-9b0a-1c7d8e9f0a1b"
                }
              }
            }
          },
          "403": {
            "description": "Permission denied, or access denied to this environment"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "removeVolumes": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "removeVolumes": false
              }
            }
          }
        }
      }
    },
    "/api/stacks/{name}/env": {
      "get": {
        "operationId": "get_api_stacks_name_env",
        "tags": [
          "stacks"
        ],
        "summary": "Get all environment variables for a stack (secrets masked)",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stack name (as shown in Dockhand, e.g. \"gitcheck\") (from GET /api/stacks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id to scope the lookup; omit for the default/legacy (null) scope (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Merged non-secret (.env file) + secret (DB, masked) variables for the stack",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "variables": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string"
                          },
                          "value": {
                            "type": "string"
                          },
                          "isSecret": {
                            "type": "boolean"
                          }
                        },
                        "required": [
                          "key",
                          "value",
                          "isSecret"
                        ]
                      }
                    }
                  },
                  "required": [
                    "variables"
                  ]
                },
                "example": {
                  "variables": [
                    {
                      "key": "GITCHECK_PORT",
                      "value": "18080",
                      "isSecret": false
                    },
                    {
                      "key": "DB_PASSWORD",
                      "value": "***",
                      "isSecret": true
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Permission denied or access denied to this environment"
          },
          "500": {
            "description": "Unexpected error while loading environment variables"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "operationId": "put_api_stacks_name_env",
        "tags": [
          "stacks"
        ],
        "summary": "Save environment variables for a stack (secrets stored encrypted in the DB, non-secrets written to the on-disk .env)",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stack name (from GET /api/stacks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "count": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "success",
                    "count"
                  ]
                },
                "example": {
                  "success": true,
                  "count": 2
                }
              }
            }
          },
          "400": {
            "description": "variables missing/not an array, or an entry has an invalid key/value"
          },
          "403": {
            "description": "Permission denied or access denied to this environment"
          },
          "500": {
            "description": "Unexpected error while saving environment variables"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "variables": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "key": {
                          "type": "string"
                        },
                        "value": {
                          "type": "string"
                        },
                        "isSecret": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "key",
                        "value"
                      ]
                    }
                  }
                },
                "required": [
                  "variables"
                ]
              },
              "example": {
                "variables": [
                  {
                    "key": "GITCHECK_MARKER",
                    "value": "marker-001",
                    "isSecret": false
                  },
                  {
                    "key": "DB_PASSWORD",
                    "value": "***",
                    "isSecret": true
                  }
                ]
              }
            }
          }
        }
      }
    },
    "/api/stacks/{name}/env/raw": {
      "get": {
        "operationId": "get_api_stacks_name_env_raw",
        "tags": [
          "stacks"
        ],
        "summary": "Get the raw .env file content as-is (comments and formatting preserved) for a stack",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stack name (from GET /api/stacks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the stack belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "content": {
                      "type": "string"
                    },
                    "noEnvFile": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "content"
                  ]
                },
                "example": {
                  "content": "FOO=bar\n# comment\nBAZ=qux\n"
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (requires stacks:view, or environment access denied on enterprise)"
          },
          "500": {
            "description": "Failed to get environment file"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "operationId": "put_api_stacks_name_env_raw",
        "tags": [
          "stacks"
        ],
        "summary": "Write raw .env file content to disk for a stack; empty content deletes the .env file, and masked \"***\" placeholders are rejected to avoid corrupting secrets",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stack name (from GET /api/stacks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the stack belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "noEnvFile": {
                      "type": "boolean"
                    },
                    "deleted": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid body (content string required) or refusal to write a masked \"***\" placeholder"
          },
          "403": {
            "description": "Permission denied (requires stacks:edit, or environment access denied on enterprise)"
          },
          "500": {
            "description": "Failed to save environment file"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string"
                  }
                },
                "required": [
                  "content"
                ]
              },
              "example": {
                "content": "FOO=bar\nBAZ=qux\n"
              }
            }
          }
        }
      }
    },
    "/api/stacks/{name}/env/validate": {
      "post": {
        "operationId": "post_api_stacks_name_env_validate",
        "tags": [
          "stacks"
        ],
        "summary": "Validate a stack's defined environment variables against the variables required/optional by its compose file (compose content and the defined variable list may be supplied in the body, otherwise loaded from the saved file/DB)",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stack name (from GET /api/stacks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the stack belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "valid": {
                      "type": "boolean"
                    },
                    "required": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "optional": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "defined": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "missing": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "unused": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "valid",
                    "required",
                    "optional",
                    "defined",
                    "missing",
                    "unused"
                  ]
                },
                "example": {
                  "valid": false,
                  "required": [
                    "IMAGE"
                  ],
                  "optional": [],
                  "defined": [],
                  "missing": [
                    "IMAGE"
                  ],
                  "unused": []
                }
              }
            }
          },
          "400": {
            "description": "No compose content provided and no saved compose file found"
          },
          "403": {
            "description": "Permission denied (requires stacks:view, or environment access denied on enterprise)"
          },
          "500": {
            "description": "Failed to validate environment variables"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "compose": {
                    "type": "string"
                  },
                  "variables": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "example": {
                "compose": "services:\n  web:\n    image: ${IMAGE}",
                "variables": [
                  "IMAGE"
                ]
              }
            }
          }
        }
      }
    },
    "/api/stacks/{name}/relocate": {
      "post": {
        "operationId": "post_api_stacks_name_relocate",
        "tags": [
          "stacks"
        ],
        "summary": "Move all stack files from the old directory to a new location, update the stored compose/env paths, and return the refreshed compose/env content",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stack name (from GET /api/stacks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the stack belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "movedFiles": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "composeContent": {
                      "type": "string"
                    },
                    "rawEnvContent": {
                      "type": "string"
                    },
                    "envVars": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string"
                          },
                          "value": {
                            "type": "string"
                          },
                          "isSecret": {
                            "type": "boolean"
                          }
                        },
                        "required": [
                          "key",
                          "value",
                          "isSecret"
                        ]
                      }
                    }
                  },
                  "required": [
                    "success",
                    "movedFiles",
                    "composeContent",
                    "rawEnvContent",
                    "envVars"
                  ]
                },
                "example": {
                  "success": true,
                  "movedFiles": [
                    "compose.yaml",
                    ".env"
                  ],
                  "composeContent": "services: {}",
                  "rawEnvContent": "FOO=bar\n",
                  "envVars": [
                    {
                      "key": "FOO",
                      "value": "bar",
                      "isSecret": false
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "oldDir and newComposePath are required, or the source directory does not exist"
          },
          "403": {
            "description": "Permission denied (requires stacks:edit)"
          },
          "500": {
            "description": "Failed to relocate stack"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "oldDir": {
                    "type": "string"
                  },
                  "newComposePath": {
                    "type": "string"
                  },
                  "newEnvPath": {
                    "type": "string"
                  }
                },
                "required": [
                  "oldDir",
                  "newComposePath"
                ]
              },
              "example": {
                "oldDir": "/opt/stacks/old",
                "newComposePath": "/opt/stacks/web/compose.yaml",
                "newEnvPath": "/opt/stacks/web/.env"
              }
            }
          }
        }
      }
    },
    "/api/stacks/{name}/restart": {
      "post": {
        "operationId": "post_api_stacks_name_restart",
        "tags": [
          "stacks"
        ],
        "summary": "Restart a stack (mode=restart) or recreate its containers (mode=recreate); progress and the final result stream over Server-Sent Events",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stack name (from GET /api/stacks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the stack belongs to (from GET /api/environments)"
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Restart mode — \"recreate\" recreates containers, anything else performs a plain restart"
          }
        ],
        "responses": {
          "200": {
            "description": "Server-Sent-Events job stream with a final result event ({success, output})"
          },
          "403": {
            "description": "Permission denied (requires stacks:restart, or environment access denied on enterprise)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/stacks/{name}/start": {
      "post": {
        "operationId": "post_api_stacks_name_start",
        "tags": [
          "stacks"
        ],
        "summary": "Start a stack (docker compose start/up), asynchronously",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stack name (from GET /api/stacks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Fire-and-forget job id — poll GET /api/jobs/{jobId} for the result. Send \"Accept: application/json\" (without text/event-stream) to instead block and receive the final {success,output|error} synchronously.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jobId": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "jobId"
                  ]
                },
                "example": {
                  "jobId": "3f9c5b1a-2e4d-4a6f-9b0a-1c7d8e9f0a1b"
                }
              }
            }
          },
          "403": {
            "description": "Permission denied, or access denied to this environment"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/stacks/{name}/stop": {
      "post": {
        "operationId": "post_api_stacks_name_stop",
        "tags": [
          "stacks"
        ],
        "summary": "Stop a stack (docker compose stop), asynchronously",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stack name (from GET /api/stacks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment id (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Fire-and-forget job id — poll GET /api/jobs/{jobId} for the result. Send \"Accept: application/json\" (without text/event-stream) to instead block and receive the final {success,output|error} synchronously.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jobId": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "jobId"
                  ]
                },
                "example": {
                  "jobId": "3f9c5b1a-2e4d-4a6f-9b0a-1c7d8e9f0a1b"
                }
              }
            }
          },
          "403": {
            "description": "Permission denied, or access denied to this environment"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/stacks/adopt": {
      "post": {
        "operationId": "post_api_stacks_adopt",
        "tags": [
          "stacks"
        ],
        "summary": "Adopt previously discovered compose stacks into Dockhand for a given environment",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "adopted": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "failed": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "error": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "name",
                          "error"
                        ]
                      }
                    }
                  },
                  "required": [
                    "adopted",
                    "failed"
                  ]
                },
                "example": {
                  "adopted": [
                    "web"
                  ],
                  "failed": []
                }
              }
            }
          },
          "400": {
            "description": "No stacks provided, missing environmentId, or a stack is missing name/composePath"
          },
          "403": {
            "description": "Permission denied (requires stacks:create)"
          },
          "500": {
            "description": "Unexpected error while adopting stacks"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "environmentId from GET /api/environments.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "stacks": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "composePath": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "name",
                        "composePath"
                      ]
                    }
                  },
                  "environmentId": {
                    "type": "integer"
                  }
                },
                "required": [
                  "stacks",
                  "environmentId"
                ]
              },
              "example": {
                "stacks": [
                  {
                    "name": "web",
                    "composePath": "/opt/stacks/web/compose.yaml"
                  }
                ],
                "environmentId": 1
              }
            }
          }
        }
      }
    },
    "/api/stacks/base-path": {
      "get": {
        "operationId": "get_api_stacks_base-path",
        "tags": [
          "stacks"
        ],
        "summary": "Return the default Dockhand stacks directory ($DATA_DIR/stacks/) where new stacks are stored by default",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "basePath": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "basePath"
                  ]
                },
                "example": {
                  "basePath": "/data/stacks"
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/stacks/default-path": {
      "get": {
        "operationId": "get_api_stacks_default-path",
        "tags": [
          "stacks"
        ],
        "summary": "Compute the default compose/env file paths for a new stack, either under a custom base location or under Dockhand's default stacks directory",
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stack name"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID (scopes the path under the environment name) (from GET /api/environments)"
          },
          {
            "name": "location",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Custom base location path"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "stackDir": {
                      "type": "string"
                    },
                    "composePath": {
                      "type": "string"
                    },
                    "envPath": {
                      "type": "string"
                    },
                    "source": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "stackDir",
                    "composePath",
                    "envPath",
                    "source"
                  ]
                },
                "example": {
                  "stackDir": "/data/stacks/prod/web",
                  "composePath": "/data/stacks/prod/web/compose.yaml",
                  "envPath": "/data/stacks/prod/web/.env",
                  "source": "default"
                }
              }
            }
          },
          "400": {
            "description": "Stack name is required"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/stacks/path-hints": {
      "get": {
        "operationId": "get_api_stacks_path-hints",
        "tags": [
          "stacks"
        ],
        "summary": "Return path hints (working directory and config file paths) extracted from a stack's Docker container labels",
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stack name (from GET /api/stacks)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the stack belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "stackName": {
                      "type": "string"
                    },
                    "workingDir": {
                      "type": "string"
                    },
                    "configFiles": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "stackName"
                  ]
                },
                "example": {
                  "stackName": "web",
                  "workingDir": "/opt/stacks/web",
                  "configFiles": [
                    "/opt/stacks/web/compose.yaml"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Stack name is required"
          },
          "401": {
            "description": "Unauthorized"
          },
          "500": {
            "description": "Failed to get path hints"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/stacks/scan": {
      "post": {
        "operationId": "post_api_stacks_scan",
        "tags": [
          "stacks"
        ],
        "summary": "Scan a given filesystem path (or all configured external paths when none is given) for compose stacks, flagging which discovered stacks are already running",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "discovered": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "name"
                        ]
                      }
                    },
                    "adopted": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "skipped": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "path": {
                            "type": "string"
                          },
                          "error": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "path",
                          "error"
                        ]
                      }
                    }
                  },
                  "required": [
                    "discovered",
                    "adopted",
                    "skipped",
                    "errors"
                  ]
                },
                "example": {
                  "discovered": [
                    {
                      "name": "web"
                    }
                  ],
                  "adopted": [],
                  "skipped": [],
                  "errors": []
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (requires stacks:create)"
          },
          "500": {
            "description": "Unexpected error while scanning"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "path": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "path": "/opt/stacks"
              }
            }
          }
        }
      }
    },
    "/api/stacks/sources": {
      "get": {
        "operationId": "get_api_stacks_sources",
        "tags": [
          "stacks"
        ],
        "summary": "Return a map of stack name to its source metadata (sourceType, composePath, repository) for the given environment",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID to scope the stacks (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Object keyed by stack name, each value carrying sourceType plus optional composePath and repository"
          },
          "403": {
            "description": "Permission denied (requires stacks:view)"
          },
          "500": {
            "description": "Failed to get stack sources"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/stacks/validate-path": {
      "post": {
        "operationId": "post_api_stacks_validate-path",
        "tags": [
          "stacks"
        ],
        "summary": "Validate a candidate external stack path (exists, is a directory, no overlap with already-configured paths); validation failures are returned as 200 with valid=false",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "valid": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "valid"
                  ]
                },
                "example": {
                  "valid": false,
                  "error": "Path does not exist"
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (requires settings:edit)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "path": {
                    "type": "string"
                  }
                },
                "required": [
                  "path"
                ]
              },
              "example": {
                "path": "/opt/external-stacks"
              }
            }
          }
        }
      }
    },
    "/api/system": {
      "get": {
        "operationId": "get_api_system",
        "tags": [
          "system"
        ],
        "summary": "Return aggregated system info — Docker daemon, host, Node.js runtime, database, and object counts for an environment",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "ID of the environment to collect Docker info for (Docker fields are null when omitted or unreachable) (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "docker": {
                      "type": "string"
                    },
                    "host": {
                      "type": "string"
                    },
                    "runtime": {
                      "type": "string"
                    },
                    "database": {
                      "type": "string"
                    },
                    "stats": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "runtime",
                    "database",
                    "stats"
                  ]
                },
                "example": {
                  "docker": {
                    "version": "27.0.3",
                    "apiVersion": "1.46"
                  },
                  "host": {
                    "name": "docker-host",
                    "cpus": 8,
                    "memory": 16777216000
                  },
                  "runtime": {
                    "runtimeName": "Node.js",
                    "platform": "linux"
                  },
                  "database": {
                    "type": "SQLite",
                    "schemaVersion": 42
                  },
                  "stats": {
                    "containers": {
                      "total": 10,
                      "running": 8,
                      "stopped": 2
                    },
                    "images": 25,
                    "volumes": 5,
                    "networks": 4,
                    "stacks": 3
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied, or (enterprise) no access to this environment"
          },
          "500": {
            "description": "Failed to fetch system info"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/system/disk": {
      "get": {
        "operationId": "get_api_system_disk",
        "tags": [
          "system"
        ],
        "summary": "Return Docker disk usage (df) for an environment, or null when collection is disabled, times out, or fails",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "ID of the environment to query disk usage for (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "diskUsage is null when SKIP_DF_COLLECTION is set, or the query times out or errors",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "diskUsage": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "diskUsage": {
                    "LayersSize": 1420000000,
                    "Images": [],
                    "Containers": [],
                    "Volumes": []
                  }
                }
              }
            }
          },
          "400": {
            "description": "Environment ID is required"
          },
          "403": {
            "description": "Permission denied, or (enterprise) no access to this environment"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/system/files": {
      "get": {
        "operationId": "get_api_system_files",
        "tags": [
          "system"
        ],
        "summary": "List the entries of a directory on Dockhand's local filesystem (protected paths are hidden)",
        "parameters": [
          {
            "name": "path",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Absolute directory path to list (defaults to \"/\")"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "path": {
                      "type": "string"
                    },
                    "parent": {
                      "type": "string"
                    },
                    "entries": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "path": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string"
                          },
                          "size": {
                            "type": "integer"
                          },
                          "mtime": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "name",
                          "path",
                          "type",
                          "size",
                          "mtime",
                          "mode"
                        ]
                      }
                    }
                  },
                  "required": [
                    "path",
                    "entries"
                  ]
                },
                "example": {
                  "path": "/docker",
                  "parent": "/",
                  "entries": [
                    {
                      "name": "stacks",
                      "path": "/docker/stacks",
                      "type": "directory",
                      "size": 4096,
                      "mtime": "2026-07-01T10:00:00.000Z",
                      "mode": "755"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The path exists but is not a directory"
          },
          "403": {
            "description": "Permission denied, or the path is protected"
          },
          "404": {
            "description": "Path not found"
          },
          "500": {
            "description": "Failed to list directory"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_system_files",
        "tags": [
          "system"
        ],
        "summary": "Create a directory on Dockhand's local filesystem (absolute path, no traversal, non-protected)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "path": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success",
                    "path"
                  ]
                },
                "example": {
                  "success": true,
                  "path": "/docker/stacks/myapp"
                }
              }
            }
          },
          "400": {
            "description": "Path is missing, not absolute, or contains \"..\""
          },
          "403": {
            "description": "Permission denied, or the path is protected"
          },
          "409": {
            "description": "Path already exists"
          },
          "500": {
            "description": "Failed to create directory"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "path": {
                    "type": "string"
                  }
                },
                "required": [
                  "path"
                ]
              },
              "example": {
                "path": "/docker/stacks/myapp"
              }
            }
          }
        }
      }
    },
    "/api/system/files/content": {
      "get": {
        "operationId": "get_api_system_files_content",
        "tags": [
          "system"
        ],
        "summary": "Read a text file from Dockhand's local filesystem (max 10MB, protected paths denied)",
        "parameters": [
          {
            "name": "path",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Absolute path of the file to read"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "path": {
                      "type": "string"
                    },
                    "content": {
                      "type": "string"
                    },
                    "size": {
                      "type": "integer"
                    },
                    "mtime": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "path",
                    "content",
                    "size",
                    "mtime"
                  ]
                },
                "example": {
                  "path": "/docker/stacks/myapp/compose.yaml",
                  "content": "services:\n  app:\n    image: nginx",
                  "size": 42,
                  "mtime": "2026-07-01T10:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "Path is missing, points to a directory, or the file exceeds 10MB"
          },
          "403": {
            "description": "Permission denied, or the path is protected"
          },
          "404": {
            "description": "File not found"
          },
          "500": {
            "description": "Failed to read file"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/templates": {
      "get": {
        "operationId": "get_api_templates",
        "tags": [
          "templates"
        ],
        "summary": "Return the normalized app templates aggregated from all enabled template sources (server-side cached for 1 hour)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string"
                      },
                      "type": {
                        "type": "string"
                      },
                      "title": {
                        "type": "string"
                      },
                      "description": {
                        "type": "string"
                      },
                      "logo": {
                        "type": "string"
                      },
                      "categories": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "source": {
                        "type": "string"
                      },
                      "image": {
                        "type": "string"
                      },
                      "projectUrl": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "id",
                      "type",
                      "title",
                      "description",
                      "logo",
                      "categories",
                      "source"
                    ]
                  }
                },
                "example": [
                  {
                    "id": "a1b2c3",
                    "type": "container",
                    "title": "Nginx",
                    "description": "Web server",
                    "logo": "https://example.com/nginx.png",
                    "categories": [
                      "web"
                    ],
                    "source": "LinuxServer.io",
                    "image": "lscr.io/linuxserver/nginx:latest",
                    "projectUrl": "https://github.com/linuxserver/docker-nginx"
                  }
                ]
              }
            }
          },
          "403": {
            "description": "Permission denied"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/templates/compose": {
      "post": {
        "operationId": "post_api_templates_compose",
        "tags": [
          "templates"
        ],
        "summary": "Return the compose YAML for a template — generated for container templates, fetched from the repository for stack templates",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "compose": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "compose"
                  ]
                },
                "example": {
                  "compose": "services:\n  nginx:\n    image: nginx:latest\n    restart: unless-stopped\n"
                }
              }
            }
          },
          "400": {
            "description": "Template is missing from the request body"
          },
          "403": {
            "description": "Permission denied"
          },
          "500": {
            "description": "Failed to generate or fetch the compose file"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "template": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string"
                      },
                      "type": {
                        "type": "string"
                      },
                      "title": {
                        "type": "string"
                      },
                      "image": {
                        "type": "string"
                      },
                      "repository": {
                        "type": "object",
                        "properties": {
                          "url": {
                            "type": "string"
                          },
                          "stackfile": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "required": [
                  "template"
                ]
              },
              "example": {
                "template": {
                  "id": "a1b2c3",
                  "type": "container",
                  "title": "Nginx",
                  "image": "nginx:latest"
                }
              }
            }
          }
        }
      }
    },
    "/api/templates/sources": {
      "get": {
        "operationId": "get_api_templates_sources",
        "tags": [
          "templates"
        ],
        "summary": "List all configured template sources (built-in and custom)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      },
                      "name": {
                        "type": "string"
                      },
                      "url": {
                        "type": "string"
                      },
                      "enabled": {
                        "type": "boolean"
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "url",
                      "enabled"
                    ]
                  }
                },
                "example": [
                  {
                    "id": 1,
                    "name": "LinuxServer.io",
                    "url": "https://fleet.linuxserver.io/api/v1/images",
                    "enabled": true
                  }
                ]
              }
            }
          },
          "403": {
            "description": "Permission denied"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_templates_sources",
        "tags": [
          "templates"
        ],
        "summary": "Add a custom template source with a name and URL",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "url",
                    "enabled"
                  ]
                },
                "example": {
                  "id": 4,
                  "name": "My Templates",
                  "url": "https://example.com/templates.json",
                  "enabled": true
                }
              }
            }
          },
          "400": {
            "description": "Name and URL are required"
          },
          "403": {
            "description": "Permission denied"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "url": {
                    "type": "string"
                  }
                },
                "required": [
                  "name",
                  "url"
                ]
              },
              "example": {
                "name": "My Templates",
                "url": "https://example.com/templates.json"
              }
            }
          }
        }
      },
      "put": {
        "operationId": "put_api_templates_sources",
        "tags": [
          "templates"
        ],
        "summary": "Update a template source — toggle enabled, or change its name/URL (only provided fields are changed)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "ok"
                  ]
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "400": {
            "description": "Missing id"
          },
          "403": {
            "description": "Permission denied"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "integer"
                  },
                  "enabled": {
                    "type": "boolean"
                  },
                  "name": {
                    "type": "string"
                  },
                  "url": {
                    "type": "string"
                  }
                },
                "required": [
                  "id"
                ]
              },
              "example": {
                "id": 3,
                "enabled": false
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_templates_sources",
        "tags": [
          "templates"
        ],
        "summary": "Delete a custom template source by ID",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "ID of the template source to delete (from GET /api/templates/sources)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "ok"
                  ]
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "400": {
            "description": "Missing id"
          },
          "403": {
            "description": "Permission denied"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/users": {
      "get": {
        "operationId": "get_api_users",
        "tags": [
          "users"
        ],
        "summary": "List all local/SSO users (any authenticated user may view — only mutations are RBAC-gated)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      },
                      "username": {
                        "type": "string"
                      },
                      "email": {
                        "type": "string"
                      },
                      "displayName": {
                        "type": "string"
                      },
                      "isAdmin": {
                        "type": "boolean"
                      },
                      "isActive": {
                        "type": "boolean"
                      },
                      "isSso": {
                        "type": "boolean"
                      },
                      "authProvider": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "id",
                      "username",
                      "isAdmin",
                      "isActive",
                      "isSso",
                      "authProvider"
                    ]
                  }
                },
                "example": [
                  {
                    "id": 1,
                    "username": "admin",
                    "email": "admin@example.com",
                    "displayName": "Admin",
                    "isAdmin": true,
                    "isActive": true,
                    "isSso": false,
                    "authProvider": "local"
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Not authenticated"
          },
          "500": {
            "description": "Unexpected error while loading users"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_users",
        "tags": [
          "users"
        ],
        "summary": "Create a local user (allowed without auth only during initial setup, before any admin exists)",
        "parameters": [],
        "responses": {
          "201": {
            "description": "The first user ever created (or every user in Free edition) automatically gets the Admin role",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "username": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string"
                    },
                    "isAdmin": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "id",
                    "username",
                    "isAdmin"
                  ]
                },
                "example": {
                  "id": 1,
                  "username": "jdoe",
                  "email": "jdoe@example.com",
                  "isAdmin": true
                }
              }
            }
          },
          "400": {
            "description": "Missing username/password, or password shorter than 8 characters"
          },
          "403": {
            "description": "Permission denied (RBAC 'users:create' missing — only applies once an admin already exists)"
          },
          "409": {
            "description": "Username already exists"
          },
          "500": {
            "description": "Unexpected error while creating the user"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "username": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  },
                  "displayName": {
                    "type": "string"
                  }
                },
                "required": [
                  "username",
                  "password"
                ]
              },
              "example": {
                "username": "jdoe",
                "email": "jdoe@example.com",
                "password": "correct horse battery staple",
                "displayName": "Jane Doe"
              }
            }
          }
        }
      }
    },
    "/api/users/{id}": {
      "get": {
        "operationId": "get_api_users_id",
        "tags": [
          "users"
        ],
        "summary": "Get a single user by id (password hash is never returned; isAdmin is derived from role assignment)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric id of the user (from GET /api/users)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "username": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string"
                    },
                    "displayName": {
                      "type": "string"
                    },
                    "mfaEnabled": {
                      "type": "boolean"
                    },
                    "isAdmin": {
                      "type": "boolean"
                    },
                    "isActive": {
                      "type": "boolean"
                    },
                    "lastLogin": {
                      "type": "string"
                    },
                    "createdAt": {
                      "type": "string"
                    },
                    "updatedAt": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "username",
                    "mfaEnabled",
                    "isAdmin",
                    "isActive",
                    "createdAt",
                    "updatedAt"
                  ]
                },
                "example": {
                  "id": 0,
                  "username": "string",
                  "email": "string",
                  "displayName": "string",
                  "mfaEnabled": true,
                  "isAdmin": true,
                  "isActive": true,
                  "lastLogin": "string",
                  "createdAt": "string",
                  "updatedAt": "string"
                }
              }
            }
          },
          "400": {
            "description": "User id is required"
          },
          "401": {
            "description": "Authentication required (auth is enabled and the caller is not authenticated)"
          },
          "404": {
            "description": "User not found"
          },
          "500": {
            "description": "Failed to read the user"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "operationId": "put_api_users_id",
        "tags": [
          "users"
        ],
        "summary": "Update a user (self-edit is always allowed; admin/active changes require admin; demoting or deactivating the last admin needs confirmDisableAuth)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric id of the user (from GET /api/users)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "username": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string"
                    },
                    "displayName": {
                      "type": "string"
                    },
                    "mfaEnabled": {
                      "type": "boolean"
                    },
                    "isAdmin": {
                      "type": "boolean"
                    },
                    "isActive": {
                      "type": "boolean"
                    },
                    "lastLogin": {
                      "type": "string"
                    },
                    "createdAt": {
                      "type": "string"
                    },
                    "updatedAt": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "username",
                    "mfaEnabled",
                    "isAdmin",
                    "isActive",
                    "createdAt",
                    "updatedAt"
                  ]
                },
                "example": {
                  "id": 0,
                  "username": "string",
                  "email": "string",
                  "displayName": "string",
                  "mfaEnabled": true,
                  "isAdmin": true,
                  "isActive": true,
                  "lastLogin": "string",
                  "createdAt": "string",
                  "updatedAt": "string"
                }
              }
            }
          },
          "400": {
            "description": "User id is required, or the new password is shorter than 8 characters"
          },
          "403": {
            "description": "Permission denied (editing another user without users:edit)"
          },
          "404": {
            "description": "User not found"
          },
          "409": {
            "description": "This is the last admin user (confirmDisableAuth required), or the username already exists"
          },
          "500": {
            "description": "Failed to update the user"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "username": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string"
                  },
                  "displayName": {
                    "type": "string"
                  },
                  "isAdmin": {
                    "type": "boolean"
                  },
                  "isActive": {
                    "type": "boolean"
                  },
                  "password": {
                    "type": "string"
                  },
                  "confirmDisableAuth": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "displayName": "Jane Doe",
                "email": "jane@example.com"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_users_id",
        "tags": [
          "users"
        ],
        "summary": "Delete a user by id; deleting the last admin while auth is enabled disables authentication and requires confirmDisableAuth",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric id of the user (from GET /api/users)"
          },
          {
            "name": "confirmDisableAuth",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Set to true to confirm deleting the last admin (which disables authentication)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "authDisabled": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true,
                  "authDisabled": true
                }
              }
            }
          },
          "400": {
            "description": "User id is required"
          },
          "403": {
            "description": "Permission denied (missing users:remove)"
          },
          "404": {
            "description": "User not found"
          },
          "409": {
            "description": "This is the last admin user — pass confirmDisableAuth=true to proceed"
          },
          "500": {
            "description": "Failed to delete the user"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/users/{id}/mfa": {
      "post": {
        "operationId": "post_api_users_id_mfa",
        "tags": [
          "users"
        ],
        "summary": "Set up MFA for a user — without a body returns a new TOTP secret/QR; with action=verify enables MFA and returns backup codes",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric id of the user (from GET /api/users)"
          }
        ],
        "responses": {
          "200": {
            "description": "Setup response ({secret, qrDataUrl}); or, for action=verify, {success, message, backupCodes}",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "secret": {
                      "type": "string"
                    },
                    "qrDataUrl": {
                      "type": "string"
                    },
                    "success": {
                      "type": "boolean"
                    },
                    "message": {
                      "type": "string"
                    },
                    "backupCodes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                },
                "example": {
                  "secret": "string",
                  "qrDataUrl": "string",
                  "success": true,
                  "message": "string",
                  "backupCodes": [
                    "string"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "User id is required, MFA token missing, or the MFA code was invalid"
          },
          "403": {
            "description": "Permission denied (may only manage own MFA unless admin)"
          },
          "404": {
            "description": "User not found"
          },
          "500": {
            "description": "Failed to set up MFA"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string"
                  },
                  "token": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "action": "verify",
                "token": "123456"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_users_id_mfa",
        "tags": [
          "users"
        ],
        "summary": "Disable MFA for a user (self or admin)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric id of the user (from GET /api/users)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "message": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success",
                    "message"
                  ]
                },
                "example": {
                  "success": true,
                  "message": "string"
                }
              }
            }
          },
          "400": {
            "description": "User id is required"
          },
          "403": {
            "description": "Permission denied (may only manage own MFA unless admin)"
          },
          "404": {
            "description": "User not found"
          },
          "500": {
            "description": "Failed to disable MFA"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/users/{id}/roles": {
      "get": {
        "operationId": "get_api_users_id_roles",
        "tags": [
          "users"
        ],
        "summary": "List the roles assigned to a user (enterprise only)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric id of the user (from GET /api/users)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "roleId": {
                        "type": "integer"
                      },
                      "name": {
                        "type": "string"
                      },
                      "environmentId": {
                        "type": "integer"
                      }
                    },
                    "required": [
                      "roleId",
                      "name"
                    ]
                  }
                },
                "example": [
                  {
                    "roleId": 0,
                    "name": "string",
                    "environmentId": 0
                  }
                ]
              }
            }
          },
          "400": {
            "description": "User id is required"
          },
          "403": {
            "description": "Enterprise license required"
          },
          "404": {
            "description": "User not found"
          },
          "500": {
            "description": "Failed to read the user roles"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_users_id_roles",
        "tags": [
          "users"
        ],
        "summary": "Assign a role to a user, optionally scoped to an environment (enterprise, admin only)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric id of the user (from GET /api/users)"
          }
        ],
        "responses": {
          "201": {
            "description": "The created user-role assignment"
          },
          "400": {
            "description": "User id or role id is required"
          },
          "403": {
            "description": "Enterprise license required, or admin access required"
          },
          "404": {
            "description": "User not found"
          },
          "500": {
            "description": "Failed to assign the role"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "roleId from GET /api/roles. environmentId from GET /api/environments.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "roleId": {
                    "type": "integer"
                  },
                  "environmentId": {
                    "type": "integer"
                  }
                },
                "required": [
                  "roleId"
                ]
              },
              "example": {
                "roleId": 3,
                "environmentId": 1
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_api_users_id_roles",
        "tags": [
          "users"
        ],
        "summary": "Remove a role assignment from a user, optionally scoped to an environment (enterprise, admin only)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric id of the user (from GET /api/users)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "User id or role id is required"
          },
          "403": {
            "description": "Enterprise license required, or admin access required"
          },
          "404": {
            "description": "Role assignment not found"
          },
          "500": {
            "description": "Failed to remove the role"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "roleId from GET /api/roles. environmentId from GET /api/environments.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "roleId": {
                    "type": "integer"
                  },
                  "environmentId": {
                    "type": "integer"
                  }
                },
                "required": [
                  "roleId"
                ]
              },
              "example": {
                "roleId": 3,
                "environmentId": 1
              }
            }
          }
        }
      }
    },
    "/api/volumes": {
      "get": {
        "operationId": "get_api_volumes",
        "tags": [
          "volumes"
        ],
        "summary": "List Docker volumes for an environment; returns an empty array when no environment is specified",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID to list volumes for (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "Name": {
                        "type": "string"
                      },
                      "Driver": {
                        "type": "string"
                      },
                      "Mountpoint": {
                        "type": "string"
                      },
                      "Scope": {
                        "type": "string"
                      },
                      "CreatedAt": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "Name",
                      "Driver"
                    ]
                  }
                },
                "example": [
                  {
                    "Name": "web_data",
                    "Driver": "local",
                    "Mountpoint": "/var/lib/docker/volumes/web_data/_data",
                    "Scope": "local",
                    "CreatedAt": "2026-06-01T10:00:00Z"
                  }
                ]
              }
            }
          },
          "403": {
            "description": "Permission denied (requires volumes:view, or environment access denied on enterprise)"
          },
          "404": {
            "description": "Environment not found"
          },
          "500": {
            "description": "Failed to list volumes"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post_api_volumes",
        "tags": [
          "volumes"
        ],
        "summary": "Create a Docker volume in an environment",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID to create the volume in (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "name": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success",
                    "name"
                  ]
                },
                "example": {
                  "success": true,
                  "name": "web_data"
                }
              }
            }
          },
          "400": {
            "description": "Volume name is required"
          },
          "403": {
            "description": "Permission denied (requires volumes:create, or environment access denied on enterprise)"
          },
          "500": {
            "description": "Failed to create volume"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "driver": {
                    "type": "string"
                  },
                  "driverOpts": {
                    "type": "object",
                    "properties": {}
                  },
                  "labels": {
                    "type": "object",
                    "properties": {}
                  }
                },
                "required": [
                  "name"
                ]
              },
              "example": {
                "name": "web_data",
                "driver": "local",
                "driverOpts": {},
                "labels": {
                  "app": "web"
                }
              }
            }
          }
        }
      }
    },
    "/api/volumes/{name}": {
      "get": {
        "operationId": "get_api_volumes_name",
        "tags": [
          "volumes"
        ],
        "summary": "Inspect a single Docker volume by name (the name is validated as a Docker identifier)",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Docker volume name (from GET /api/volumes)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the volume belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "Name": {
                      "type": "string"
                    },
                    "Driver": {
                      "type": "string"
                    },
                    "Mountpoint": {
                      "type": "string"
                    },
                    "Scope": {
                      "type": "string"
                    },
                    "Labels": {
                      "type": "object",
                      "properties": {}
                    },
                    "Options": {
                      "type": "object",
                      "properties": {}
                    },
                    "CreatedAt": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "Name",
                    "Driver",
                    "Mountpoint"
                  ]
                },
                "example": {
                  "Name": "web_data",
                  "Driver": "local",
                  "Mountpoint": "/var/lib/docker/volumes/web_data/_data",
                  "Scope": "local",
                  "Labels": {},
                  "Options": {},
                  "CreatedAt": "2026-06-01T10:00:00Z"
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (requires volumes:inspect, or environment access denied on enterprise)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "delete": {
        "operationId": "delete_api_volumes_name",
        "tags": [
          "volumes"
        ],
        "summary": "Remove a Docker volume by name, optionally forcing removal",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Docker volume name (from GET /api/volumes)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the volume belongs to (from GET /api/environments)"
          },
          {
            "name": "force",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Force removal of the volume"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (requires volumes:remove, or environment access denied on enterprise)"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/volumes/{name}/browse": {
      "get": {
        "operationId": "get_api_volumes_name_browse",
        "tags": [
          "volumes"
        ],
        "summary": "Browse a directory inside a Docker volume via a cached helper container; the volume is mounted read-only when in use by other containers",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Docker volume name (from GET /api/volumes)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the volume belongs to (from GET /api/environments)"
          },
          {
            "name": "path",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Directory path inside the volume to list (defaults to \"/\")"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "path": {
                      "type": "string"
                    },
                    "entries": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string"
                          },
                          "size": {
                            "type": "integer"
                          },
                          "permissions": {
                            "type": "string"
                          },
                          "owner": {
                            "type": "string"
                          },
                          "group": {
                            "type": "string"
                          },
                          "modified": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "name",
                          "type",
                          "size",
                          "permissions",
                          "owner",
                          "group",
                          "modified"
                        ]
                      }
                    },
                    "usage": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "containerId": {
                            "type": "string"
                          },
                          "containerName": {
                            "type": "string"
                          },
                          "state": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "containerId",
                          "containerName",
                          "state"
                        ]
                      }
                    },
                    "isInUse": {
                      "type": "boolean"
                    },
                    "helperId": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "path",
                    "entries",
                    "usage",
                    "isInUse",
                    "helperId"
                  ]
                },
                "example": {
                  "path": "/",
                  "entries": [
                    {
                      "name": "data",
                      "type": "directory",
                      "size": 4096,
                      "permissions": "drwxr-xr-x",
                      "owner": "root",
                      "group": "root",
                      "modified": "2026-06-01 10:00"
                    }
                  ],
                  "usage": [],
                  "isInUse": false,
                  "helperId": "abc123"
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (requires volumes:inspect) or permission denied accessing the path"
          },
          "404": {
            "description": "Directory not found"
          },
          "500": {
            "description": "Failed to browse volume"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/volumes/{name}/browse/content": {
      "get": {
        "operationId": "get_api_volumes_name_browse_content",
        "tags": [
          "volumes"
        ],
        "summary": "Read the content of a single file inside a Docker volume (files larger than 1MB are rejected)",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Docker volume name (from GET /api/volumes)"
          },
          {
            "name": "path",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "File path inside the volume to read"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the volume belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "content": {
                      "type": "string"
                    },
                    "path": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "content",
                    "path"
                  ]
                },
                "example": {
                  "content": "hello world\n",
                  "path": "/data/readme.txt"
                }
              }
            }
          },
          "400": {
            "description": "Path is required, or the path points to a directory"
          },
          "403": {
            "description": "Permission denied (requires volumes:inspect) or permission denied reading the file"
          },
          "404": {
            "description": "File not found"
          },
          "413": {
            "description": "File is too large to view (max 1MB)"
          },
          "500": {
            "description": "Failed to read file"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/volumes/{name}/browse/release": {
      "post": {
        "operationId": "post_api_volumes_name_browse_release",
        "tags": [
          "volumes"
        ],
        "summary": "Release the cached helper container used to browse a Docker volume",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Docker volume name (from GET /api/volumes)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the volume belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (requires volumes:inspect)"
          },
          "500": {
            "description": "Failed to release volume helper"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/volumes/{name}/clone": {
      "post": {
        "operationId": "post_api_volumes_name_clone",
        "tags": [
          "volumes"
        ],
        "summary": "Clone a Docker volume into a new named volume, copying the data with a temporary helper container and preserving driver/options/labels",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Source Docker volume name (from GET /api/volumes)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the volume belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "name": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success",
                    "name"
                  ]
                },
                "example": {
                  "success": true,
                  "name": "web_data_copy"
                }
              }
            }
          },
          "400": {
            "description": "New volume name is required"
          },
          "403": {
            "description": "Permission denied (requires volumes:create)"
          },
          "500": {
            "description": "Failed to clone volume"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              },
              "example": {
                "name": "web_data_copy"
              }
            }
          }
        }
      }
    },
    "/api/volumes/{name}/export": {
      "get": {
        "operationId": "get_api_volumes_name_export",
        "tags": [
          "volumes"
        ],
        "summary": "Export a Docker volume (or a sub-path) as a downloadable archive — streaming tar, gzip-compressed tar.gz, or raw single-file bytes",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Docker volume name (from GET /api/volumes)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the volume belongs to (from GET /api/environments)"
          },
          {
            "name": "path",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Sub-path inside the volume to export (defaults to \"/\")"
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Archive format: \"tar\" (default), \"tar.gz\", or \"raw\" (single file)"
          }
        ],
        "responses": {
          "200": {
            "description": "Binary archive stream (Content-Disposition attachment); Content-Type is application/x-tar, application/gzip, or application/octet-stream depending on format"
          },
          "403": {
            "description": "Permission denied (requires volumes:inspect)"
          },
          "404": {
            "description": "Path not found"
          },
          "500": {
            "description": "Failed to export volume"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/volumes/{name}/inspect": {
      "get": {
        "operationId": "get_api_volumes_name_inspect",
        "tags": [
          "volumes"
        ],
        "summary": "Inspect a Docker volume by name, returning the raw Docker volume inspect object",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Docker volume name (from GET /api/volumes)"
          },
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Environment ID the volume belongs to (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "Name": {
                      "type": "string"
                    },
                    "Driver": {
                      "type": "string"
                    },
                    "Mountpoint": {
                      "type": "string"
                    },
                    "Scope": {
                      "type": "string"
                    },
                    "Labels": {
                      "type": "object",
                      "properties": {}
                    },
                    "Options": {
                      "type": "object",
                      "properties": {}
                    },
                    "CreatedAt": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "Name",
                    "Driver",
                    "Mountpoint"
                  ]
                },
                "example": {
                  "Name": "web_data",
                  "Driver": "local",
                  "Mountpoint": "/var/lib/docker/volumes/web_data/_data",
                  "Scope": "local",
                  "Labels": {},
                  "Options": {},
                  "CreatedAt": "2026-06-01T10:00:00Z"
                }
              }
            }
          },
          "403": {
            "description": "Permission denied (requires volumes:inspect)"
          },
          "500": {
            "description": "Failed to inspect volume"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/vulnerabilities": {
      "get": {
        "operationId": "get_api_vulnerabilities",
        "tags": [
          "vulnerabilities"
        ],
        "summary": "A filtered, sorted page of aggregated vulnerability findings for an environment",
        "parameters": [],
        "responses": {
          "200": {
            "description": "A page of findings plus the filtered total count; a permission failure returns the status from the access check",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "findings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "findings",
                    "total"
                  ]
                },
                "example": {
                  "findings": [
                    {
                      "cve": "CVE-2024-0001",
                      "severity": "high",
                      "package": "openssl",
                      "imageName": "nginx:latest"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "description": "Accepts limit, offset, q, severity, image, container, stack, sort and dir query params (parsed centrally). Returns an empty page when no environment resolves."
      }
    },
    "/api/vulnerabilities/count": {
      "get": {
        "operationId": "get_api_vulnerabilities_count",
        "tags": [
          "vulnerabilities"
        ],
        "summary": "Return the total finding count, severity summary, and distinct image/container/stack filter values for an environment",
        "parameters": [],
        "responses": {
          "200": {
            "description": "An empty metadata object is returned when no environment resolves; a permission failure returns the status from the access check",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "total": {
                      "type": "integer"
                    },
                    "summary": {
                      "type": "string"
                    },
                    "images": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "containers": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "stacks": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "total",
                    "summary",
                    "images",
                    "containers",
                    "stacks"
                  ]
                },
                "example": {
                  "total": 12,
                  "summary": {
                    "critical": 1,
                    "high": 3,
                    "medium": 5,
                    "low": 3
                  },
                  "images": [
                    "nginx:latest"
                  ],
                  "containers": [
                    "web"
                  ],
                  "stacks": [
                    "frontend"
                  ]
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/vulnerabilities/export": {
      "get": {
        "operationId": "get_api_vulnerabilities_export",
        "tags": [
          "vulnerabilities"
        ],
        "summary": "Export an environment's aggregated vulnerability findings as json or csv, matching the grid's filters and sort",
        "parameters": [
          {
            "name": "format",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Output format — json (default) or csv"
          }
        ],
        "responses": {
          "200": {
            "description": "A permission failure returns the status from the access check; 500 is returned on an export error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/vulnerabilities/scan-all": {
      "post": {
        "operationId": "post_api_vulnerabilities_scan-all",
        "tags": [
          "vulnerabilities"
        ],
        "summary": "Scan every image in an environment for vulnerabilities, streaming per-image progress as Server-Sent Events",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "ID of the environment whose images to scan (from GET /api/environments)"
          }
        ],
        "responses": {
          "200": {
            "description": "A Server-Sent Events stream of progress and per-image results, ending with a summary \"result\" event"
          },
          "403": {
            "description": "Permission denied, or (enterprise) no access to this environment"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/audit": {
      "get": {
        "operationId": "get_audit",
        "tags": [
          "audit"
        ],
        "summary": "List audit log entries with optional filters (Enterprise only)",
        "parameters": [
          {
            "name": "username",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": ""
          },
          {
            "name": "entity_type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": ""
          },
          {
            "name": "action",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": ""
          },
          {
            "name": "environment_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": ""
          },
          {
            "name": "from_date",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": ""
          },
          {
            "name": "to_date",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": ""
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": ""
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": ""
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "logs": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "userId": {
                            "type": "integer"
                          },
                          "username": {
                            "type": "string"
                          },
                          "action": {
                            "type": "string"
                          },
                          "entityType": {
                            "type": "string"
                          },
                          "entityId": {
                            "type": "string"
                          },
                          "entityName": {
                            "type": "string"
                          },
                          "environmentId": {
                            "type": "integer"
                          },
                          "description": {
                            "type": "string"
                          },
                          "details": {
                            "type": "object",
                            "properties": {}
                          },
                          "ipAddress": {
                            "type": "string"
                          },
                          "userAgent": {
                            "type": "string"
                          },
                          "createdAt": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "id",
                          "username",
                          "action",
                          "entityType",
                          "createdAt"
                        ]
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "logs",
                    "total",
                    "limit",
                    "offset"
                  ]
                },
                "example": {
                  "logs": [
                    {
                      "id": 0,
                      "userId": 0,
                      "username": "string",
                      "action": "string",
                      "entityType": "string",
                      "entityId": "string",
                      "entityName": "string",
                      "environmentId": 0,
                      "description": "string",
                      "details": {},
                      "ipAddress": "string",
                      "userAgent": "string",
                      "createdAt": "string"
                    }
                  ],
                  "total": 0,
                  "limit": 0,
                  "offset": 0
                }
              }
            }
          },
          "403": {
            "description": "Enterprise license required, or permission denied to view the audit log"
          },
          "500": {
            "description": "Failed to fetch audit logs"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/audit/users": {
      "get": {
        "operationId": "get_audit_users",
        "tags": [
          "audit"
        ],
        "summary": "List distinct usernames that appear in the audit log, for use as a filter dropdown (Enterprise only)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "example": [
                  "string"
                ]
              }
            }
          },
          "403": {
            "description": "Enterprise license required, or permission denied to view the audit log"
          },
          "500": {
            "description": "Failed to fetch users"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/metrics": {
      "get": {
        "operationId": "get_metrics",
        "tags": [
          "metrics"
        ],
        "summary": "Prometheus metrics endpoint (disabled and 404s unless EXPORT_METRICS=true; public if app auth is disabled, otherwise requires a session or bearer token)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Prometheus text-exposition-format metrics body"
          },
          "401": {
            "description": "Not authenticated (session or bearer token required when app auth is enabled)"
          },
          "404": {
            "description": "Metrics export disabled (EXPORT_METRICS is not true)"
          },
          "500": {
            "description": "Metrics collection failed"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    }
  }
}