{ "openapi": "3.1.0", "info": { "title": "BanGUI", "description": "Web interface for monitoring, managing, and configuring fail2ban.", "version": "0.9.19-rc.1" }, "paths": { "/api/v1/health": { "get": { "tags": [ "Health" ], "summary": "Application health check", "description": "Return application and component status.\n\nPerforms lightweight checks on key application components and returns\nHTTP 200 if all healthy, HTTP 503 if fail2ban is offline.\n\nDocker/orchestration health checks interpret 503 as unhealthy and restart\nthe container if fail2ban remains unreachable.\n\nArgs:\n app_state: Injected application state containing runtime components.\n server_status: Injected cached server status snapshot.\n\nReturns:\n HTTP 200 with :class:`~app.models.response.HealthResponse` when healthy,\n HTTP 503 with :class:`~app.models.response.HealthResponse` when fail2ban\n is offline.", "operationId": "health_check_api_v1_health_get", "responses": { "200": { "description": "All components healthy", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HealthResponse" } } } }, "503": { "description": "fail2ban offline or component degraded" } } } }, "/api/v1/health/live": { "get": { "tags": [ "Health" ], "summary": "Process liveness probe", "description": "Lightweight liveness check for Kubernetes.\n\nReturns 200 when the Python process and event loop are responsive.\nA non-2xx response tells Kubernetes to restart the container.\nNo subsystem checks are performed \u2014 this endpoint must be fast.", "operationId": "liveness_probe_api_v1_health_live_get", "responses": { "200": { "description": "Process is alive", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReadyResponse" } } } } } } }, "/api/v1/health/ready": { "get": { "tags": [ "Health" ], "summary": "Subsystem readiness probe", "description": "Readiness check for Kubernetes.\n\nVerifies all critical sub-systems are reachable:\n- Database connectivity\n- fail2ban socket (via cached server status)\n- Config directory read access\n- Background scheduler liveness\n\nReturns HTTP 200 only when every check passes; returns HTTP 503 with a\nJSON body listing every failed subsystem otherwise. Each check has a\nshort per-subsystem timeout to prevent the endpoint from overwhelming the\nsystem under load.", "operationId": "readiness_probe_api_v1_health_ready_get", "responses": { "200": { "description": "All subsystems healthy", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReadyResponse" } } } }, "503": { "description": "One or more subsystems unreachable" } } } }, "/api/v1/setup": { "get": { "tags": [ "setup" ], "summary": "Check whether setup has been completed", "description": "Return whether the initial setup wizard has been completed.\n\nReturns:\n :class:`~app.models.setup.SetupStatusResponse` with ``completed``\n set to ``True`` if setup is done, ``False`` otherwise.", "operationId": "get_setup_status_api_v1_setup_get", "responses": { "200": { "description": "Setup status returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SetupStatusResponse" } } } } } }, "post": { "tags": [ "setup" ], "summary": "Run the initial setup wizard", "description": "Persist the initial BanGUI configuration.\n\nArgs:\n app: The FastAPI application instance.\n body: Setup request payload validated by Pydantic.\n settings_ctx: Settings service context containing db and repository.\n\nReturns:\n :class:`~app.models.setup.SetupResponse` on success.\n\nRaises:\n SetupAlreadyCompleteError: if setup has already been completed.", "operationId": "post_setup_api_v1_setup_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SetupRequest" } } }, "required": true }, "responses": { "201": { "description": "Setup completed successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SetupResponse" } } } }, "400": { "description": "Validation error in request body" }, "409": { "description": "Setup already completed" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/setup/timezone": { "get": { "tags": [ "setup" ], "summary": "Return the configured IANA timezone", "description": "Return the IANA timezone configured during the initial setup wizard.\n\nThe frontend uses this to convert UTC timestamps to the local time zone\nchosen by the administrator.\n\nReturns:\n :class:`~app.models.setup.SetupTimezoneResponse` with ``timezone``\n set to the stored IANA identifier (e.g. ``\"UTC\"`` or\n ``\"Europe/Berlin\"``), defaulting to ``\"UTC\"`` if unset.", "operationId": "get_timezone_api_v1_setup_timezone_get", "responses": { "200": { "description": "Timezone returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SetupTimezoneResponse" } } } } } } }, "/api/v1/auth/login": { "post": { "tags": [ "auth" ], "summary": "Authenticate with the master password", "description": "Verify the master password and return a session token.\n\nOn success the token is also set as an ``HttpOnly`` ``SameSite=Lax``\ncookie so the browser SPA benefits from automatic credential handling.\n\nCache invalidation: On successful login, any existing cached sessions for\nthe same user are invalidated so that stale tokens (e.g., from a stolen\ndevice) cannot be reused beyond the cache TTL window.\n\nArgs:\n body: Login request validated by Pydantic.\n response: FastAPI response object used to set the cookie.\n request: The incoming HTTP request (used to extract client IP).\n session_ctx: Session service context containing db and repository.\n settings: Application settings (used for session duration and trusted proxies).\n session_cache: Session cache for invalidating old sessions on login.\n\nReturns:\n :class:`~app.models.auth.LoginResponse` containing the token.\n\nRaises:\n AuthenticationError: if the password is incorrect.", "operationId": "login_api_v1_auth_login_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LoginRequest" } } }, "required": true }, "responses": { "200": { "description": "Login successful", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LoginResponse" } } } }, "401": { "description": "Invalid password" }, "422": { "description": "Validation error \u2014 invalid request body" }, "503": { "description": "Setup not complete" } } } }, "/api/v1/auth/session": { "get": { "tags": [ "auth" ], "summary": "Validate the current session", "description": "Validate the current session.\n\nThis endpoint requires a valid session and returns 200 if the session is\nvalid and still active. If the session is invalid, expired, or missing,\nFastAPI's ``require_auth`` dependency returns 401 automatically.\n\nThe frontend calls this on mount to bootstrap its authentication state\nfrom the backend rather than relying solely on cached ``sessionStorage``.\n\nArgs:\n _: The injected session object (unused, but its presence triggers validation).\n\nReturns:\n :class:`~app.models.auth.SessionValidResponse` confirming the session state.", "operationId": "validate_session_api_v1_auth_session_get", "responses": { "200": { "description": "Session valid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SessionValidResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" } } } }, "/api/v1/auth/logout": { "post": { "tags": [ "auth" ], "summary": "Revoke the current session", "description": "Invalidate the active session.\n\nThe session token is read from the ``bangui_session`` cookie or the\n``Authorization: Bearer`` header. If no token is present the request\nis silently treated as a successful logout (idempotent).\n\nArgs:\n request: FastAPI request (used to extract the token).\n response: FastAPI response (used to clear the cookie).\n session_ctx: Session service context containing db and repository.\n settings: Application settings (used to unwrap signed tokens).\n session_cache: Session cache for invalidation.\n\nReturns:\n :class:`~app.models.auth.LogoutResponse`.", "operationId": "logout_api_v1_auth_logout_post", "responses": { "200": { "description": "Logout successful", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LogoutResponse" } } } }, "401": { "description": "Session missing or invalid (silently successful)" } } } }, "/api/v1/dashboard/status": { "get": { "tags": [ "Dashboard" ], "summary": "Return the cached fail2ban server status", "description": "Return the most recent fail2ban health snapshot.\n\nThe snapshot is populated by a background task that runs every 30 seconds.\nIf the task has not yet executed a placeholder ``online=False`` status is\nreturned so the response is always well-formed.\n\nArgs:\n server_status: Cached fail2ban server health snapshot (injected).\n _auth: Validated session \u2014 enforces authentication on this endpoint.\n\nReturns:\n :class:`~app.models.server.ServerStatusResponse` containing the\n current health snapshot.", "operationId": "get_server_status_api_v1_dashboard_status_get", "responses": { "200": { "description": "Server status returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ServerStatusResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "502": { "description": "fail2ban unreachable" } } } }, "/api/v1/dashboard/bans": { "get": { "tags": [ "Dashboard" ], "summary": "Return a paginated list of recent bans", "description": "Return a paginated list of bans within the selected time window.\n\nReads from the fail2ban database and enriches each entry with\ngeolocation data (country, ASN, organisation) from the ip-api.com\nfree API. Results are sorted newest-first. Geo lookups are served\nfrom the in-memory cache only; no database writes occur during this\nGET request.\n\nArgs:\n _auth: Validated session dependency.\n ban_ctx: Ban service context containing db and repository.\n socket_path: Path to fail2ban Unix domain socket.\n http_session: Shared HTTP session for geolocation.\n geo_cache: Geolocation cache instance.\n range: Time-range preset \u2014 ``\"24h\"``, ``\"7d\"``, ``\"30d\"``, or\n ``\"365d\"``.\n page: 1-based page number.\n page_size: Maximum items per page (1\u2013500).\n origin: Optional filter by ban origin.\n\nReturns:\n :class:`~app.models.ban.DashboardBanListResponse` with paginated\n ban items and the total count for the selected window.", "operationId": "get_dashboard_bans_api_v1_dashboard_bans_get", "parameters": [ { "name": "range", "in": "query", "required": false, "schema": { "enum": [ "24h", "7d", "30d", "365d" ], "type": "string", "description": "Time-range preset.", "default": "24h", "title": "Range" }, "description": "Time-range preset." }, { "name": "source", "in": "query", "required": false, "schema": { "enum": [ "fail2ban", "archive" ], "type": "string", "description": "Data source: 'fail2ban' or 'archive'.", "default": "fail2ban", "title": "Source" }, "description": "Data source: 'fail2ban' or 'archive'." }, { "name": "page", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1, "description": "1-based page number.", "default": 1, "title": "Page" }, "description": "1-based page number." }, { "name": "page_size", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1, "description": "Items per page.", "default": 100, "title": "Page Size" }, "description": "Items per page." }, { "name": "origin", "in": "query", "required": false, "schema": { "anyOf": [ { "enum": [ "blocklist", "selfblock" ], "type": "string" }, { "type": "null" } ], "description": "Filter by ban origin: 'blocklist' or 'selfblock'. Omit for all.", "title": "Origin" }, "description": "Filter by ban origin: 'blocklist' or 'selfblock'. Omit for all." } ], "responses": { "200": { "description": "Ban list returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DashboardBanListResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/dashboard/bans/by-country": { "get": { "tags": [ "Dashboard" ], "summary": "Return ban counts aggregated by country", "description": "Return ban counts aggregated by ISO country code.\n\nUses SQL aggregation (``GROUP BY ip``) and batch geo-resolution to handle\n10 000+ banned IPs efficiently. Returns a ``{country_code: count}`` map\nand the 200 most recent raw ban rows for the companion access table. Geo\nlookups are served from the in-memory cache only; no database writes occur\nduring this GET request.\n\nArgs:\n _auth: Validated session dependency.\n ban_ctx: Ban service context containing db and repository.\n socket_path: Path to fail2ban Unix domain socket.\n http_session: Shared HTTP session for geolocation.\n geo_cache: Geolocation cache instance.\n range: Time-range preset.\n origin: Optional filter by ban origin.\n\nReturns:\n :class:`~app.models.ban.BansByCountryResponse` with per-country\n aggregation and the companion ban list.", "operationId": "get_bans_by_country_api_v1_dashboard_bans_by_country_get", "parameters": [ { "name": "range", "in": "query", "required": false, "schema": { "enum": [ "24h", "7d", "30d", "365d" ], "type": "string", "description": "Time-range preset.", "default": "24h", "title": "Range" }, "description": "Time-range preset." }, { "name": "source", "in": "query", "required": false, "schema": { "enum": [ "fail2ban", "archive" ], "type": "string", "description": "Data source: 'fail2ban' or 'archive'.", "default": "fail2ban", "title": "Source" }, "description": "Data source: 'fail2ban' or 'archive'." }, { "name": "origin", "in": "query", "required": false, "schema": { "anyOf": [ { "enum": [ "blocklist", "selfblock" ], "type": "string" }, { "type": "null" } ], "description": "Filter by ban origin: 'blocklist' or 'selfblock'. Omit for all.", "title": "Origin" }, "description": "Filter by ban origin: 'blocklist' or 'selfblock'. Omit for all." }, { "name": "country_code", "in": "query", "required": false, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "description": "ISO alpha-2 country code to filter companion rows.", "title": "Country Code" }, "description": "ISO alpha-2 country code to filter companion rows." } ], "responses": { "200": { "description": "Ban counts by country returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BansByCountryResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/dashboard/bans/trend": { "get": { "tags": [ "Dashboard" ], "summary": "Return ban counts aggregated into time buckets", "description": "Return ban counts grouped into equal-width time buckets.\n\nEach bucket represents a contiguous time interval within the selected\nwindow. All buckets are returned \u2014 empty buckets (zero bans) are\nincluded so the frontend always receives a complete, gap-free series\nsuitable for rendering a continuous area or line chart.\n\nBucket sizes:\n\n* ``24h`` \u2192 1-hour buckets (24 total)\n* ``7d`` \u2192 6-hour buckets (28 total)\n* ``30d`` \u2192 1-day buckets (30 total)\n* ``365d`` \u2192 7-day buckets (~53 total)\n\nArgs:\n _auth: Validated session dependency.\n ban_ctx: Ban service context containing db and repository.\n socket_path: Path to fail2ban Unix domain socket.\n range: Time-range preset.\n origin: Optional filter by ban origin.\n\nReturns:\n :class:`~app.models.ban.BanTrendResponse` with the ordered bucket\n list and the bucket-size label.", "operationId": "get_ban_trend_api_v1_dashboard_bans_trend_get", "parameters": [ { "name": "range", "in": "query", "required": false, "schema": { "enum": [ "24h", "7d", "30d", "365d" ], "type": "string", "description": "Time-range preset.", "default": "24h", "title": "Range" }, "description": "Time-range preset." }, { "name": "source", "in": "query", "required": false, "schema": { "enum": [ "fail2ban", "archive" ], "type": "string", "description": "Data source: 'fail2ban' or 'archive'.", "default": "fail2ban", "title": "Source" }, "description": "Data source: 'fail2ban' or 'archive'." }, { "name": "origin", "in": "query", "required": false, "schema": { "anyOf": [ { "enum": [ "blocklist", "selfblock" ], "type": "string" }, { "type": "null" } ], "description": "Filter by ban origin: 'blocklist' or 'selfblock'. Omit for all.", "title": "Origin" }, "description": "Filter by ban origin: 'blocklist' or 'selfblock'. Omit for all." } ], "responses": { "200": { "description": "Ban trend data returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BanTrendResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/dashboard/bans/by-jail": { "get": { "tags": [ "Dashboard" ], "summary": "Return ban counts aggregated by jail", "description": "Return ban counts grouped by jail name for the selected time window.\n\nQueries the fail2ban database and returns a list of jails sorted by\nban count descending. This endpoint is intended for the dashboard jail\ndistribution bar chart.\n\nArgs:\n _auth: Validated session dependency.\n ban_ctx: Ban service context containing db and repository.\n socket_path: Path to fail2ban Unix domain socket.\n range: Time-range preset \u2014 ``\"24h\"``, ``\"7d\"``, ``\"30d\"``, or\n ``\"365d\"``.\n origin: Optional filter by ban origin.\n\nReturns:\n :class:`~app.models.ban.BansByJailResponse` with per-jail counts\n sorted descending and the total for the selected window.", "operationId": "get_bans_by_jail_api_v1_dashboard_bans_by_jail_get", "parameters": [ { "name": "range", "in": "query", "required": false, "schema": { "enum": [ "24h", "7d", "30d", "365d" ], "type": "string", "description": "Time-range preset.", "default": "24h", "title": "Range" }, "description": "Time-range preset." }, { "name": "source", "in": "query", "required": false, "schema": { "enum": [ "fail2ban", "archive" ], "type": "string", "description": "Data source: 'fail2ban' or 'archive'.", "default": "fail2ban", "title": "Source" }, "description": "Data source: 'fail2ban' or 'archive'." }, { "name": "origin", "in": "query", "required": false, "schema": { "anyOf": [ { "enum": [ "blocklist", "selfblock" ], "type": "string" }, { "type": "null" } ], "description": "Filter by ban origin: 'blocklist' or 'selfblock'. Omit for all.", "title": "Origin" }, "description": "Filter by ban origin: 'blocklist' or 'selfblock'. Omit for all." } ], "responses": { "200": { "description": "Ban counts by jail returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BansByJailResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/jails": { "get": { "tags": [ "Jails" ], "summary": "List all active fail2ban jails", "description": "Return a summary of every active fail2ban jail.\n\nIncludes runtime metrics (currently banned, total bans, failures) and\nkey configuration (find time, ban time, max retries, backend, idle state)\nfor each jail.\n\nArgs:\n _auth: Validated session \u2014 enforces authentication.\n socket_path: Path to the fail2ban Unix domain socket.\n state: The jail service state holder.\n\nReturns:\n :class:`~app.models.jail.JailListResponse` with all active jails.", "operationId": "get_jails_api_v1_jails_get", "responses": { "200": { "description": "Jails list returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailListResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "502": { "description": "fail2ban unreachable" } } } }, "/api/v1/jails/{name}": { "get": { "tags": [ "Jails" ], "summary": "Return full detail for a single jail", "description": "Return the complete configuration and runtime state for one jail.\n\nIncludes log paths, fail regex and ignore regex patterns, date pattern,\nlog encoding, attached action names, ban-time settings, and runtime\ncounters.\n\nArgs:\n _auth: Validated session \u2014 enforces authentication.\n name: Jail name.\n\nReturns:\n :class:`~app.models.jail.JailDetailResponse` with the full jail.\n\nRaises:\n HTTPException: 404 when the jail does not exist.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "get_jail_api_v1_jails__name__get", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." } ], "responses": { "200": { "description": "Jail detail returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailDetailResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail not found" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/jails/reload-all": { "post": { "tags": [ "Jails" ], "summary": "Reload all fail2ban jails", "description": "Reload every fail2ban jail to apply configuration changes.\n\nThis command instructs fail2ban to re-read its configuration for all\njails simultaneously.\n\nArgs:\n _auth: Validated session \u2014 enforces authentication.\n\nReturns:\n :class:`~app.models.jail.JailCommandResponse` confirming the reload.\n\nRaises:\n HTTPException: 502 when fail2ban is unreachable.\n HTTPException: 409 when fail2ban reports the operation failed.", "operationId": "reload_all_jails_api_v1_jails_reload_all_post", "responses": { "200": { "description": "All jails reloaded", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailCommandResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "409": { "description": "fail2ban reports operation failed" }, "502": { "description": "fail2ban unreachable" } } } }, "/api/v1/jails/{name}/start": { "post": { "tags": [ "Jails" ], "summary": "Start a stopped jail", "description": "Start a fail2ban jail that is currently stopped.\n\nArgs:\n _auth: Validated session \u2014 enforces authentication.\n name: Jail name.\n\nReturns:\n :class:`~app.models.jail.JailCommandResponse` confirming the start.\n\nRaises:\n HTTPException: 404 when the jail does not exist.\n HTTPException: 409 when fail2ban reports the operation failed.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "start_jail_api_v1_jails__name__start_post", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." } ], "responses": { "200": { "description": "Jail started", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailCommandResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail not found" }, "409": { "description": "fail2ban reports operation failed" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/jails/{name}/stop": { "post": { "tags": [ "Jails" ], "summary": "Stop a running jail", "description": "Stop a running fail2ban jail.\n\nThe jail will no longer monitor logs or issue new bans. Existing bans\nmay or may not be removed depending on fail2ban configuration. If the\njail is already stopped the request succeeds silently (idempotent).\n\nArgs:\n _auth: Validated session \u2014 enforces authentication.\n name: Jail name.\n\nReturns:\n :class:`~app.models.jail.JailCommandResponse` confirming the stop.\n\nRaises:\n HTTPException: 409 when fail2ban reports the operation failed.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "stop_jail_api_v1_jails__name__stop_post", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." } ], "responses": { "200": { "description": "Jail stopped", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailCommandResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "409": { "description": "fail2ban reports operation failed" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/jails/{name}/idle": { "post": { "tags": [ "Jails" ], "summary": "Toggle idle mode for a jail", "description": "Enable or disable idle mode for a fail2ban jail.\n\nIn idle mode the jail suspends log monitoring without fully stopping,\npreserving all existing bans.\n\nArgs:\n _auth: Validated session \u2014 enforces authentication.\n name: Jail name.\n on: ``true`` to enable idle, ``false`` to disable.\n\nReturns:\n :class:`~app.models.jail.JailCommandResponse` confirming the change.\n\nRaises:\n HTTPException: 404 when the jail does not exist.\n HTTPException: 409 when fail2ban reports the operation failed.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "toggle_idle_api_v1_jails__name__idle_post", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "boolean", "description": "``true`` to enable idle, ``false`` to disable.", "title": "On" } } } }, "responses": { "200": { "description": "Idle mode toggled", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailCommandResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail not found" }, "409": { "description": "fail2ban reports operation failed" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/jails/{name}/reload": { "post": { "tags": [ "Jails" ], "summary": "Reload a single jail", "description": "Reload a single fail2ban jail to pick up configuration changes.\n\nArgs:\n _auth: Validated session \u2014 enforces authentication.\n name: Jail name.\n\nReturns:\n :class:`~app.models.jail.JailCommandResponse` confirming the reload.\n\nRaises:\n HTTPException: 404 when the jail does not exist.\n HTTPException: 409 when fail2ban reports the operation failed.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "reload_jail_api_v1_jails__name__reload_post", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." } ], "responses": { "200": { "description": "Jail reloaded", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailCommandResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail not found" }, "409": { "description": "fail2ban reports operation failed" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/jails/{name}/ignoreip": { "get": { "tags": [ "Jails" ], "summary": "List the ignore IPs for a jail", "description": "Return the current ignore list (IP whitelist) for a fail2ban jail.\n\nArgs:\n _auth: Validated session \u2014 enforces authentication.\n name: Jail name.\n\nReturns:\n List of IP addresses and CIDR networks on the ignore list.\n\nRaises:\n HTTPException: 404 when the jail does not exist.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "get_ignore_list_api_v1_jails__name__ignoreip_get", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." } ], "responses": { "200": { "description": "Ignore list returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/IgnoreListResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail not found" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } }, "post": { "tags": [ "Jails" ], "summary": "Add an IP or network to the ignore list", "description": "Add an IP address or CIDR network to a jail's ignore list.\n\nIPs on the ignore list are never banned by that jail, even if they\ntrigger the configured fail regex.\n\nArgs:\n _auth: Validated session \u2014 enforces authentication.\n name: Jail name.\n body: Payload containing the IP or CIDR to add.\n\nReturns:\n :class:`~app.models.jail.JailCommandResponse` confirming the addition.\n\nRaises:\n HTTPException: 400 when the IP address or network is invalid.\n HTTPException: 404 when the jail does not exist.\n HTTPException: 409 when fail2ban reports the operation failed.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "add_ignore_ip_api_v1_jails__name__ignoreip_post", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/IgnoreIpRequest" } } } }, "responses": { "201": { "description": "IP added to ignore list", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailCommandResponse" } } } }, "400": { "description": "IP or network invalid" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail not found" }, "409": { "description": "fail2ban reports operation failed" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } }, "delete": { "tags": [ "Jails" ], "summary": "Remove an IP or network from the ignore list", "description": "Remove an IP address or CIDR network from a jail's ignore list.\n\nArgs:\n _auth: Validated session \u2014 enforces authentication.\n name: Jail name.\n body: Payload containing the IP or CIDR to remove.\n\nReturns:\n :class:`~app.models.jail.JailCommandResponse` confirming the removal.\n\nRaises:\n HTTPException: 404 when the jail does not exist.\n HTTPException: 409 when fail2ban reports the operation failed.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "del_ignore_ip_api_v1_jails__name__ignoreip_delete", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/IgnoreIpRequest" } } } }, "responses": { "200": { "description": "IP removed from ignore list", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailCommandResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail not found" }, "409": { "description": "fail2ban reports operation failed" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/jails/{name}/ignoreself": { "post": { "tags": [ "Jails" ], "summary": "Toggle the ignoreself option for a jail", "description": "Toggle the ``ignoreself`` flag for a fail2ban jail.\n\nWhen ``ignoreself`` is enabled fail2ban automatically adds the server's\nown IP addresses to the ignore list so the host can never ban itself.\n\nArgs:\n _auth: Validated session \u2014 enforces authentication.\n name: Jail name.\n on: ``true`` to enable, ``false`` to disable.\n\nReturns:\n :class:`~app.models.jail.JailCommandResponse` confirming the change.\n\nRaises:\n HTTPException: 404 when the jail does not exist.\n HTTPException: 409 when fail2ban reports the operation failed.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "toggle_ignore_self_api_v1_jails__name__ignoreself_post", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "boolean", "description": "``true`` to enable ignoreself, ``false`` to disable.", "title": "On" } } } }, "responses": { "200": { "description": "ignoreself toggled", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailCommandResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail not found" }, "409": { "description": "fail2ban reports operation failed" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/jails/{name}/banned": { "get": { "tags": [ "Jails" ], "summary": "Return paginated currently-banned IPs for a single jail", "description": "Return a paginated list of IPs currently banned by a specific jail.\n\nThe full ban list is fetched from the fail2ban socket, filtered by the\noptional *search* substring, sliced to the requested page, and then\ngeo-enriched exclusively for that page slice.\n\nArgs:\n _auth: Validated session \u2014 enforces authentication.\n ban_ctx: Ban service context containing db and repository.\n name: Jail name.\n socket_path: Path to fail2ban Unix domain socket.\n http_session: Shared HTTP session for geolocation.\n geo_cache: Geolocation cache instance.\n page: 1-based page number (default 1, min 1).\n page_size: Items per page (default 100, max 100).\n search: Optional case-insensitive substring filter on the IP address.\n\nReturns:\n :class:`~app.models.ban.JailBannedIpsResponse` with the paginated bans.\n\nRaises:\n HTTPException: 400 when *page* or *page_size* are out of range.\n HTTPException: 404 when the jail does not exist.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "get_jail_banned_ips_api_v1_jails__name__banned_get", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." }, { "name": "page", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1, "description": "1-based page number.", "default": 1, "title": "Page" }, "description": "1-based page number." }, { "name": "page_size", "in": "query", "required": false, "schema": { "type": "integer", "maximum": 100, "minimum": 1, "description": "Items per page (max 100).", "default": 100, "title": "Page Size" }, "description": "Items per page (max 100)." }, { "name": "search", "in": "query", "required": false, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Search" } } ], "responses": { "200": { "description": "Banned IPs returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailBannedIpsResponse" } } } }, "400": { "description": "page or page_size out of range" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail not found" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/bans/active": { "get": { "tags": [ "Bans" ], "summary": "List all currently banned IPs across all jails", "description": "Return every IP that is currently banned across all fail2ban jails.\n\nEach entry includes the jail name, ban start time, expiry time, and\nenriched geolocation data (country code).\n\nArgs:\n request: Incoming request (used to access ``app.state``).\n _auth: Validated session \u2014 enforces authentication.\n ban_ctx: Ban service context containing db and repository.\n socket_path: Path to fail2ban Unix domain socket.\n http_session: Shared HTTP session for geolocation.\n geo_cache: Geolocation cache instance.\n\nReturns:\n :class:`~app.models.ban.ActiveBanListResponse` with all active bans.\n\nRaises:\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "get_active_bans_api_v1_bans_active_get", "responses": { "200": { "description": "Active ban list returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ActiveBanListResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "502": { "description": "fail2ban unreachable" } } } }, "/api/v1/bans": { "post": { "tags": [ "Bans" ], "summary": "Ban an IP address in a specific jail", "description": "Ban an IP address in the specified fail2ban jail.\n\nThe IP address is validated before the command is sent. IPv4 and\nIPv6 addresses are both accepted.\n\nArgs:\n request: Incoming request (used to access ``app.state``).\n _auth: Validated session \u2014 enforces authentication.\n body: Payload containing the IP address and target jail.\n\nReturns:\n :class:`~app.models.jail.JailCommandResponse` confirming the ban.\n\nRaises:\n HTTPException: 400 when the IP address is invalid.\n HTTPException: 404 when the specified jail does not exist.\n HTTPException: 409 when fail2ban reports the ban failed.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "ban_ip_api_v1_bans_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BanRequest" } } }, "required": true }, "responses": { "201": { "description": "IP banned successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailCommandResponse" } } } }, "400": { "description": "Invalid IP address" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail not found" }, "409": { "description": "Ban command failed in fail2ban" }, "429": { "description": "Rate limit exceeded for ban operations" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } }, "delete": { "tags": [ "Bans" ], "summary": "Unban an IP address from one or all jails", "description": "Unban an IP address from a specific jail or all jails.\n\nWhen ``unban_all`` is ``true`` the IP is removed from every jail using\nfail2ban's global unban command. When ``jail`` is specified only that\njail is targeted. If neither ``unban_all`` nor ``jail`` is provided the\nIP is unbanned from all jails (equivalent to ``unban_all=true``).\n\nArgs:\n request: Incoming request (used to access ``app.state``).\n _auth: Validated session \u2014 enforces authentication.\n body: Payload with the IP address, optional jail, and unban_all flag.\n\nReturns:\n :class:`~app.models.jail.JailCommandResponse` confirming the unban.\n\nRaises:\n HTTPException: 400 when the IP address is invalid.\n HTTPException: 404 when the specified jail does not exist.\n HTTPException: 409 when fail2ban reports the unban failed.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "unban_ip_api_v1_bans_delete", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnbanRequest" } } }, "required": true }, "responses": { "200": { "description": "IP unbanned successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailCommandResponse" } } } }, "400": { "description": "Invalid IP address" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail not found" }, "409": { "description": "Unban command failed in fail2ban" }, "429": { "description": "Rate limit exceeded for unban operations" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/bans/all": { "delete": { "tags": [ "Bans" ], "summary": "Unban every currently banned IP across all jails", "description": "Remove all active bans from every fail2ban jail in a single operation.\n\nUses fail2ban's ``unban --all`` command to atomically clear every active\nban across all jails. Returns the number of IPs that were unbanned.\n\nArgs:\n request: Incoming request (used to access ``app.state``).\n _auth: Validated session \u2014 enforces authentication.\n\nReturns:\n :class:`~app.models.ban.UnbanAllResponse` with the count of\n unbanned IPs.\n\nRaises:\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "unban_all_api_v1_bans_all_delete", "responses": { "200": { "description": "All bans cleared", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnbanAllResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "502": { "description": "fail2ban unreachable" } } } }, "/api/v1/geo/lookup/{ip}": { "get": { "tags": [ "Geo" ], "summary": "Look up ban status and geo information for an IP", "description": "Return current ban status, geo data, and network information for an IP.\n\nChecks every running fail2ban jail to determine whether the IP is\ncurrently banned, and enriches the result with country, ASN, and\norganisation data from ip-api.com.\n\nArgs:\n _auth: Validated session \u2014 enforces authentication.\n ip: The IP address to look up.\n\nReturns:\n :class:`~app.models.geo.IpLookupResponse` with ban status and geo data.\n\nRaises:\n HTTPException: 400 when *ip* is not a valid IP address.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "lookup_ip_api_v1_geo_lookup__ip__get", "parameters": [ { "name": "ip", "in": "path", "required": true, "schema": { "type": "string", "description": "IPv4 or IPv6 address to look up.", "title": "Ip" }, "description": "IPv4 or IPv6 address to look up." } ], "responses": { "200": { "description": "IP lookup result returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/IpLookupResponse" } } } }, "400": { "description": "Invalid IP address" }, "401": { "description": "Session missing, expired, or invalid" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/geo/stats": { "get": { "tags": [ "Geo" ], "summary": "Geo cache diagnostic counters", "description": "Return diagnostic counters for the geo cache subsystem.\n\nUseful for operators and the UI to gauge geo-resolution health.\n\nArgs:\n _auth: Validated session \u2014 enforces authentication.\n ban_ctx: Ban service context containing db and repository.\n\nReturns:\n :class:`~app.models.geo.GeoCacheStatsResponse` with current counters.", "operationId": "geo_stats_api_v1_geo_stats_get", "responses": { "200": { "description": "Geo cache stats returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GeoCacheStatsResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" } } } }, "/api/v1/geo/re-resolve": { "post": { "tags": [ "Geo" ], "summary": "Re-resolve all IPs whose country could not be determined", "description": "Retry geo resolution for every IP in ``geo_cache`` with a null country.\n\nClears the in-memory negative cache first so that previously failing IPs\nare immediately eligible for a new API attempt.\n\nArgs:\n _auth: Validated session \u2014 enforces authentication.\n ban_ctx: Ban service context containing db and repository.\n http_session: Shared HTTP session for geo lookups.\n\nReturns:\n A :class:`~app.models.geo.GeoReResolveResponse` with retry counts.", "operationId": "re_resolve_geo_api_v1_geo_re_resolve_post", "responses": { "200": { "description": "Re-resolve result", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GeoReResolveResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" } } } }, "/api/v1/config/jails": { "get": { "tags": [ "Config", "Jail Config" ], "summary": "List configuration for all active jails", "description": "Return editable configuration for every active fail2ban jail.\n\nFetches ban time, find time, max retries, regex patterns, log paths,\ndate pattern, encoding, backend, and attached actions for all jails.\n\nArgs:\n request: Incoming request (used to access ``app.state``).\n _auth: Validated session \u2014 enforces authentication.\n\nReturns:\n :class:`~app.models.config.JailConfigListResponse`.", "operationId": "get_jail_configs_api_v1_config_jails_get", "responses": { "200": { "description": "Jail configs returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailConfigListResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "502": { "description": "fail2ban unreachable" } } } }, "/api/v1/config/jails/inactive": { "get": { "tags": [ "Config", "Jail Config" ], "summary": "List all inactive jails discovered in config files", "description": "Return all jails defined in fail2ban config files that are not running.\n\nParses ``jail.conf``, ``jail.local``, and ``jail.d/`` following the\nfail2ban merge order. Jails that fail2ban currently reports as running\nare excluded; only truly inactive entries are returned.\n\nArgs:\n request: FastAPI request object.\n _auth: Validated session \u2014 enforces authentication.\n\nReturns:\n :class:`~app.models.config.InactiveJailListResponse`.", "operationId": "get_inactive_jails_api_v1_config_jails_inactive_get", "responses": { "200": { "description": "Inactive jail list returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InactiveJailListResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "502": { "description": "fail2ban unreachable" } } } }, "/api/v1/config/jails/pending-recovery": { "get": { "tags": [ "Config", "Jail Config" ], "summary": "Return active crash-recovery record if one exists", "description": "Return the current :class:`~app.models.config.PendingRecovery` record.\n\nA non-null response means fail2ban crashed shortly after a jail activation\nand the user should be offered a rollback option. Returns ``null`` (HTTP\n200 with ``null`` body) when no recovery is pending.\n\nArgs:\n request: FastAPI request object.\n _auth: Validated session.\n\nReturns:\n :class:`~app.models.config.PendingRecovery` or ``None``.", "operationId": "get_pending_recovery_api_v1_config_jails_pending_recovery_get", "responses": { "200": { "description": "Recovery record or null", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PendingRecovery", "anyOf": [ { "$ref": "#/components/schemas/PendingRecovery" }, { "type": "null" } ], "title": "Response Get Pending Recovery Api V1 Config Jails Pending Recovery Get" } } } }, "401": { "description": "Session missing, expired, or invalid" } } } }, "/api/v1/config/jails/{name}": { "get": { "tags": [ "Config", "Jail Config" ], "summary": "Return configuration for a single jail", "description": "Return the full editable configuration for one fail2ban jail.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n name: Jail name.\n\nReturns:\n :class:`~app.models.config.JailConfigResponse`.\n\nRaises:\n HTTPException: 404 when the jail does not exist.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "get_jail_config_api_v1_config_jails__name__get", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." } ], "responses": { "200": { "description": "Jail config returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailConfigResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail not found" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } }, "put": { "tags": [ "Config", "Jail Config" ], "summary": "Update jail configuration", "description": "Update one or more configuration fields for an active fail2ban jail.\n\nRegex patterns are validated before being sent to fail2ban. An invalid\npattern returns 422 with the regex error message.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n name: Jail name.\n body: Partial update \u2014 only non-None fields are written.\n\nRaises:\n HTTPException: 404 when the jail does not exist.\n HTTPException: 422 when a regex pattern fails to compile.\n HTTPException: 400 when a set command is rejected.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "update_jail_config_api_v1_config_jails__name__put", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailConfigUpdate" } } } }, "responses": { "204": { "description": "Jail config updated successfully" }, "400": { "description": "Set command rejected or invalid regex" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail not found" }, "422": { "description": "Regex pattern failed to compile" }, "429": { "description": "Rate limit exceeded for jail update operations" }, "502": { "description": "fail2ban unreachable" } } } }, "/api/v1/config/jails/{name}/logpath": { "post": { "tags": [ "Config", "Jail Config" ], "summary": "Add a log file path to an existing jail", "description": "Register an additional log file for an existing jail to monitor.\n\nUses ``set addlogpath `` to add the path\nwithout requiring a daemon restart.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n name: Jail name.\n body: Log path and tail/head preference.\n\nRaises:\n HTTPException: 404 when the jail does not exist.\n HTTPException: 400 when the command is rejected or path is invalid.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "add_log_path_api_v1_config_jails__name__logpath_post", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AddLogPathRequest" } } } }, "responses": { "204": { "description": "Log path added successfully" }, "400": { "description": "Command rejected or path invalid" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail not found" }, "429": { "description": "Rate limit exceeded for jail create operations" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } }, "delete": { "tags": [ "Config", "Jail Config" ], "summary": "Remove a monitored log path from a jail", "description": "Stop a jail from monitoring the specified log file.\n\nUses ``set dellogpath `` to remove the log path at runtime\nwithout requiring a daemon restart.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n name: Jail name.\n log_path: Absolute path to the log file to remove (query parameter).\n\nRaises:\n HTTPException: 422 when the log path is outside allowed directories.\n HTTPException: 404 when the jail does not exist.\n HTTPException: 400 when the command is rejected.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "delete_log_path_api_v1_config_jails__name__logpath_delete", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." }, { "name": "log_path", "in": "query", "required": true, "schema": { "type": "string", "description": "Absolute path of the log file to stop monitoring.", "title": "Log Path" }, "description": "Absolute path of the log file to stop monitoring." } ], "responses": { "204": { "description": "Log path removed successfully" }, "400": { "description": "Command rejected" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail not found" }, "422": { "description": "Log path outside allowed directories" }, "429": { "description": "Rate limit exceeded for jail delete operations" }, "502": { "description": "fail2ban unreachable" } } } }, "/api/v1/config/jails/{name}/activate": { "post": { "tags": [ "Config", "Jail Config" ], "summary": "Activate an inactive jail", "description": "Enable an inactive jail and reload fail2ban.\n\nWrites ``enabled = true`` (plus any override values from the request\nbody) to ``jail.d/{name}.local`` and triggers a full fail2ban reload so\nthe jail starts immediately.\n\nArgs:\n app: FastAPI application instance.\n _auth: Validated session.\n config_dir: Absolute path to the fail2ban configuration directory.\n socket_path: Path to the fail2ban Unix domain socket.\n health_probe: Injectable health probe function for checking fail2ban status.\n name: Name of the jail to activate.\n body: Optional override values (bantime, findtime, maxretry, port,\n logpath).\n\nReturns:\n :class:`~app.models.config.JailActivationResponse`.\n\nRaises:\n HTTPException: 400 if *name* contains invalid characters.\n HTTPException: 404 if *name* is not found in any config file.\n HTTPException: 409 if the jail is already active.\n HTTPException: 502 if fail2ban is unreachable.", "operationId": "activate_jail_api_v1_config_jails__name__activate_post", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." } ], "requestBody": { "content": { "application/json": { "schema": { "anyOf": [ { "$ref": "#/components/schemas/ActivateJailRequest" }, { "type": "null" } ], "title": "Body" } } } }, "responses": { "200": { "description": "Jail activated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailActivationResponse" } } } }, "400": { "description": "Invalid jail name" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail not found in config files" }, "409": { "description": "Jail already active" }, "429": { "description": "Rate limit exceeded for jail activate operations" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/jails/{name}/deactivate": { "post": { "tags": [ "Config", "Jail Config" ], "summary": "Deactivate an active jail", "description": "Disable an active jail and reload fail2ban.\n\nWrites ``enabled = false`` to ``jail.d/{name}.local`` and triggers a\nfull fail2ban reload so the jail stops immediately.\n\nArgs:\n _auth: Validated session.\n config_dir: Absolute path to the fail2ban configuration directory.\n socket_path: Path to the fail2ban Unix domain socket.\n health_probe: Injectable health probe function for checking fail2ban status.\n name: Name of the jail to deactivate.\n\nReturns:\n :class:`~app.models.config.JailActivationResponse`.\n\nRaises:\n HTTPException: 400 if *name* contains invalid characters.\n HTTPException: 404 if *name* is not found in any config file.\n HTTPException: 409 if the jail is already inactive.\n HTTPException: 502 if fail2ban is unreachable.", "operationId": "deactivate_jail_api_v1_config_jails__name__deactivate_post", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." } ], "responses": { "200": { "description": "Jail deactivated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailActivationResponse" } } } }, "400": { "description": "Invalid jail name" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail not found in config files" }, "409": { "description": "Jail already inactive" }, "429": { "description": "Rate limit exceeded for jail deactivate operations" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/jails/{name}/local": { "delete": { "tags": [ "Config", "Jail Config" ], "summary": "Delete the jail.d override file for an inactive jail", "description": "Remove the ``jail.d/{name}.local`` override file for an inactive jail.\n\nThis endpoint is the clean-up action for inactive jails that still carry\na ``.local`` override file (e.g. one written with ``enabled = false`` by a\nprevious deactivation). The file is deleted without modifying fail2ban's\nrunning state, since the jail is already inactive.\n\nArgs:\n request: FastAPI request object.\n _auth: Validated session.\n name: Name of the jail whose ``.local`` file should be removed.\n\nRaises:\n HTTPException: 400 if *name* contains invalid characters.\n HTTPException: 404 if *name* is not found in any config file.\n HTTPException: 409 if the jail is currently active.\n HTTPException: 500 if the file cannot be deleted.\n HTTPException: 502 if fail2ban is unreachable.", "operationId": "delete_jail_local_override_api_v1_config_jails__name__local_delete", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." } ], "responses": { "204": { "description": "Override file deleted successfully" }, "400": { "description": "Invalid jail name" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail not found in config files" }, "409": { "description": "Jail currently active" }, "500": { "description": "File cannot be deleted" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/jails/{name}/validate": { "post": { "tags": [ "Config", "Jail Config" ], "summary": "Validate jail configuration before activation", "description": "Run pre-activation validation checks on a jail configuration.\n\nValidates filter and action file existence, regex pattern compilation, and\nlog path existence without modifying any files or reloading fail2ban.\n\nArgs:\n request: FastAPI request object.\n _auth: Validated session.\n name: Jail name to validate.\n\nReturns:\n :class:`~app.models.config.JailValidationResult` with any issues found.\n\nRaises:\n HTTPException: 400 if *name* contains invalid characters.\n HTTPException: 404 if *name* is not found in any config file.", "operationId": "validate_jail_api_v1_config_jails__name__validate_post", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." } ], "responses": { "200": { "description": "Validation result", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailValidationResult" } } } }, "400": { "description": "Invalid jail name" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail not found in config files" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/jails/{name}/rollback": { "post": { "tags": [ "Config", "Jail Config" ], "summary": "Disable a bad jail config and restart fail2ban", "description": "Disable the specified jail and attempt to restart fail2ban.\n\nWrites ``enabled = false`` to ``jail.d/{name}.local`` (works even when\nfail2ban is down \u2014 no socket is needed), then runs the configured start\ncommand and waits up to ten seconds for the daemon to come back online.\n\nOn success, clears the :class:`~app.models.config.PendingRecovery` record.\n\nArgs:\n _auth: Validated session.\n app: FastAPI application instance.\n name: Jail name to disable and roll back.\n\nReturns:\n :class:`~app.models.config.RollbackResponse`.\n\nRaises:\n HTTPException: 400 if *name* contains invalid characters.\n HTTPException: 500 if writing the .local override file fails.", "operationId": "rollback_jail_api_v1_config_jails__name__rollback_post", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." } ], "responses": { "200": { "description": "Rollback completed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RollbackResponse" } } } }, "400": { "description": "Invalid jail name" }, "401": { "description": "Session missing, expired, or invalid" }, "500": { "description": "Failed to write .local override file" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/jails/{name}/filter": { "post": { "tags": [ "Config", "Jail Config" ], "summary": "Assign a filter to a jail", "description": "Write ``filter = {filter_name}`` to the jail's ``.local`` config.\n\nExisting keys in the jail's ``.local`` file are preserved. If the file\ndoes not exist it is created.\n\nArgs:\n request: FastAPI request object.\n _auth: Validated session.\n name: Jail name.\n body: Filter to assign.\n reload: When ``true``, trigger a fail2ban reload after writing.\n\nRaises:\n HTTPException: 400 if *name* or *filter_name* contain invalid characters.\n HTTPException: 404 if the jail or filter does not exist.\n HTTPException: 500 if writing fails.", "operationId": "assign_filter_to_jail_api_v1_config_jails__name__filter_post", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." }, { "name": "reload", "in": "query", "required": false, "schema": { "type": "boolean", "description": "Reload fail2ban after assigning.", "default": false, "title": "Reload" }, "description": "Reload fail2ban after assigning." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssignFilterRequest" } } } }, "responses": { "204": { "description": "Filter assigned successfully" }, "400": { "description": "Invalid jail name or filter name" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail or filter not found" }, "429": { "description": "Rate limit exceeded for jail create operations" }, "500": { "description": "Failed to write .local override file" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/jails/{name}/action": { "post": { "tags": [ "Config", "Jail Config" ], "summary": "Add an action to a jail", "description": "Append an action entry to the jail's ``.local`` config.\n\nExisting keys in the jail's ``.local`` file are preserved. If the file\ndoes not exist it is created. The action is not duplicated if it is\nalready present.\n\nArgs:\n request: FastAPI request object.\n _auth: Validated session.\n name: Jail name.\n body: Action to add plus optional per-jail parameters.\n reload: When ``true``, trigger a fail2ban reload after writing.\n\nRaises:\n HTTPException: 400 if *name* or *action_name* contain invalid characters.\n HTTPException: 404 if the jail or action does not exist.\n HTTPException: 500 if writing fails.", "operationId": "assign_action_to_jail_api_v1_config_jails__name__action_post", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." }, { "name": "reload", "in": "query", "required": false, "schema": { "type": "boolean", "description": "Reload fail2ban after assigning.", "default": false, "title": "Reload" }, "description": "Reload fail2ban after assigning." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssignActionRequest" } } } }, "responses": { "204": { "description": "Action added successfully" }, "400": { "description": "Invalid jail name or action name" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail or action not found" }, "429": { "description": "Rate limit exceeded for jail create operations" }, "500": { "description": "Failed to write .local override file" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/jails/{name}/action/{action_name}": { "delete": { "tags": [ "Config", "Jail Config" ], "summary": "Remove an action from a jail", "description": "Remove an action from the jail's ``.local`` config.\n\nIf the jail has no ``.local`` file or the action is not listed there,\nthe call is silently idempotent.\n\nArgs:\n request: FastAPI request object.\n _auth: Validated session.\n name: Jail name.\n action_name: Base name of the action to remove.\n reload: When ``true``, trigger a fail2ban reload after writing.\n\nRaises:\n HTTPException: 400 if *name* or *action_name* contain invalid characters.\n HTTPException: 404 if the jail is not found in config files.\n HTTPException: 500 if writing fails.", "operationId": "remove_action_from_jail_api_v1_config_jails__name__action__action_name__delete", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Jail name as configured in fail2ban.", "title": "Name" }, "description": "Jail name as configured in fail2ban." }, { "name": "action_name", "in": "path", "required": true, "schema": { "type": "string", "description": "Action base name to remove.", "title": "Action Name" }, "description": "Action base name to remove." }, { "name": "reload", "in": "query", "required": false, "schema": { "type": "boolean", "description": "Reload fail2ban after removing.", "default": false, "title": "Reload" }, "description": "Reload fail2ban after removing." } ], "responses": { "204": { "description": "Action removed successfully" }, "400": { "description": "Invalid jail name or action name" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail not found in config files" }, "429": { "description": "Rate limit exceeded for jail delete operations" }, "500": { "description": "Failed to write .local override file" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/filters": { "get": { "tags": [ "Config", "Filter Config" ], "summary": "List all available filters with active/inactive status", "description": "Return all filters discovered in ``filter.d/`` with active/inactive status.\n\nScans ``{config_dir}/filter.d/`` for ``.conf`` files, merges any\ncorresponding ``.local`` overrides, and cross-references each filter's\nname against the ``filter`` fields of currently running jails to determine\nwhether it is active.\n\nActive filters (those used by at least one running jail) are sorted to the\ntop of the list; inactive filters follow. Both groups are sorted\nalphabetically within themselves.\n\nArgs:\n request: FastAPI request object.\n _auth: Validated session \u2014 enforces authentication.\n\nReturns:\n :class:`~app.models.config.FilterListResponse` with all discovered\n filters.", "operationId": "list_filters_api_v1_config_filters_get", "responses": { "200": { "description": "Filter list returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FilterListResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "502": { "description": "fail2ban unreachable" } } }, "post": { "tags": [ "Config", "Filter Config" ], "summary": "Create a new user-defined filter", "description": "Create a new user-defined filter at ``filter.d/{name}.local``.\n\nThe filter is created as a ``.local`` file so it can coexist safely with\nshipped ``.conf`` files. Returns 409 if a ``.conf`` or ``.local`` for\nthe requested name already exists.\n\nAll regex patterns are validated before writing. Validation includes:\n\n- **Length limit**: Patterns must not exceed 1000 characters (prevents DoS)\n- **Compilation timeout**: Pattern compilation must complete within 2 seconds\n (prevents ReDoS attacks via catastrophic backtracking)\n- **Syntax validation**: Patterns must be valid Python regex\n\nArgs:\n request: FastAPI request object.\n _auth: Validated session.\n body: Filter name and ``[Definition]`` fields.\n reload: When ``true``, trigger a fail2ban reload after creating.\n\nReturns:\n :class:`~app.models.config.FilterConfig` for the new filter.\n\nRaises:\n HTTPException: 400 if the name contains invalid characters.\n HTTPException: 400 if any regex pattern exceeds 1000 characters.\n HTTPException: 400 if any regex pattern times out during compilation (ReDoS).\n HTTPException: 409 if the filter already exists.\n HTTPException: 422 if any regex pattern is invalid.\n HTTPException: 500 if writing fails.", "operationId": "create_filter_api_v1_config_filters_post", "parameters": [ { "name": "reload", "in": "query", "required": false, "schema": { "type": "boolean", "description": "Reload fail2ban after creating.", "default": false, "title": "Reload" }, "description": "Reload fail2ban after creating." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FilterCreateRequest" } } } }, "responses": { "201": { "description": "Filter created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FilterConfig" } } } }, "400": { "description": "Invalid filter name or regex too long" }, "401": { "description": "Session missing, expired, or invalid" }, "409": { "description": "Filter already exists" }, "422": { "description": "Regex pattern failed to compile" }, "429": { "description": "Rate limit exceeded for filter create operations" }, "500": { "description": "Failed to write .local file" } } } }, "/api/v1/config/filters/{name}": { "get": { "tags": [ "Config", "Filter Config" ], "summary": "Return full parsed detail for a single filter", "description": "Return the full parsed configuration and active/inactive status for one filter.\n\nReads ``{config_dir}/filter.d/{name}.conf``, merges any corresponding\n``.local`` override, and annotates the result with ``active``,\n``used_by_jails``, ``source_file``, and ``has_local_override``.\n\nArgs:\n request: FastAPI request object.\n _auth: Validated session \u2014 enforces authentication.\n name: Filter base name (with or without ``.conf`` extension).\n\nReturns:\n :class:`~app.models.config.FilterConfig`.\n\nRaises:\n HTTPException: 404 if the filter is not found in ``filter.d/``.\n HTTPException: 502 if fail2ban is unreachable.", "operationId": "get_filter_api_v1_config_filters__name__get", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Filter base name, e.g. ``sshd`` or ``sshd.conf``.", "title": "Name" }, "description": "Filter base name, e.g. ``sshd`` or ``sshd.conf``." } ], "responses": { "200": { "description": "Filter config returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FilterConfig" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Filter not found in filter.d/" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } }, "put": { "tags": [ "Config", "Filter Config" ], "summary": "Update a filter's .local override with new regex/pattern values", "description": "Update a filter's ``[Definition]`` fields by writing a ``.local`` override.\n\nAll regex patterns are validated before writing. Validation includes:\n\n- **Length limit**: Patterns must not exceed 1000 characters (prevents DoS)\n- **Compilation timeout**: Pattern compilation must complete within 2 seconds\n (prevents ReDoS attacks via catastrophic backtracking)\n- **Syntax validation**: Patterns must be valid Python regex\n\nThe original ``.conf`` file is never modified. Fields left as ``null`` in the\nrequest body are kept at their current values.\n\nArgs:\n request: FastAPI request object.\n _auth: Validated session.\n name: Filter base name (with or without ``.conf`` extension).\n body: Partial update \u2014 ``failregex``, ``ignoreregex``, ``datepattern``,\n ``journalmatch``.\n reload: When ``true``, trigger a fail2ban reload after writing.\n\nReturns:\n Updated :class:`~app.models.config.FilterConfig`.\n\nRaises:\n HTTPException: 400 if *name* contains invalid characters.\n HTTPException: 400 if any regex pattern exceeds 1000 characters.\n HTTPException: 400 if any regex pattern times out during compilation (ReDoS).\n HTTPException: 422 if any regex pattern fails to compile.\n HTTPException: 404 if the filter does not exist.\n HTTPException: 500 if writing the ``.local`` file fails.", "operationId": "update_filter_api_v1_config_filters__name__put", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Filter base name, e.g. ``sshd`` or ``sshd.conf``.", "title": "Name" }, "description": "Filter base name, e.g. ``sshd`` or ``sshd.conf``." }, { "name": "reload", "in": "query", "required": false, "schema": { "type": "boolean", "description": "Reload fail2ban after writing.", "default": false, "title": "Reload" }, "description": "Reload fail2ban after writing." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FilterUpdateRequest" } } } }, "responses": { "200": { "description": "Filter updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FilterConfig" } } } }, "400": { "description": "Invalid filter name" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Filter not found" }, "422": { "description": "Regex pattern failed to compile" }, "429": { "description": "Rate limit exceeded for filter update operations" }, "500": { "description": "Failed to write .local file" } } }, "delete": { "tags": [ "Config", "Filter Config" ], "summary": "Delete a user-created filter's .local file", "description": "Delete a user-created filter's ``.local`` override file.\n\nShipped ``.conf``-only filters cannot be deleted (returns 409). When\nboth a ``.conf`` and a ``.local`` exist, only the ``.local`` is removed.\nWhen only a ``.local`` exists (user-created filter), the file is deleted\nentirely.\n\nArgs:\n request: FastAPI request object.\n _auth: Validated session.\n name: Filter base name.\n\nRaises:\n HTTPException: 400 if *name* contains invalid characters.\n HTTPException: 404 if the filter does not exist.\n HTTPException: 409 if the filter is a shipped default (conf-only).\n HTTPException: 500 if deletion fails.", "operationId": "delete_filter_api_v1_config_filters__name__delete", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Filter base name, e.g. ``sshd`` or ``sshd.conf``.", "title": "Name" }, "description": "Filter base name, e.g. ``sshd`` or ``sshd.conf``." } ], "responses": { "204": { "description": "Filter deleted successfully" }, "400": { "description": "Invalid filter name" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Filter not found" }, "409": { "description": "Filter is a shipped default (conf-only)" }, "429": { "description": "Rate limit exceeded for filter delete operations" }, "500": { "description": "Failed to delete .local file" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/actions": { "get": { "tags": [ "Config" ], "summary": "List all action definition files", "description": "Return a list of every ``.conf`` and ``.local`` file in ``action.d/``.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n\nReturns:\n :class:`~app.models.file_config.ConfFilesResponse`.", "operationId": "list_action_files_api_v1_config_actions_get", "responses": { "200": { "description": "Action files returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConfFilesResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "503": { "description": "Config directory unavailable" } } }, "post": { "tags": [ "Config" ], "summary": "Create a new action definition file", "description": "Create a new ``.conf`` file in ``action.d/``.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n body: Name and initial content for the new file.\n\nReturns:\n The created :class:`~app.models.file_config.ConfFileContent`.\n\nRaises:\n HTTPException: 400 if *name* is invalid or content exceeds limit.\n HTTPException: 409 if a file with that name already exists.\n HTTPException: 503 if the config directory is unavailable.", "operationId": "create_action_file_api_v1_config_actions_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConfFileCreateRequest" } } }, "required": true }, "responses": { "201": { "description": "Action file created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConfFileContent" } } } }, "400": { "description": "Name invalid or content exceeds limit" }, "401": { "description": "Session missing, expired, or invalid" }, "409": { "description": "File with that name already exists" }, "503": { "description": "Config directory unavailable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/actions/{name}": { "get": { "tags": [ "Config", "Action Config" ], "summary": "Return full parsed detail for a single action", "description": "Return the full parsed configuration and active/inactive status for one action.\n\nReads ``{config_dir}/action.d/{name}.conf``, merges any corresponding\n``.local`` override, and annotates the result with ``active``,\n``used_by_jails``, ``source_file``, and ``has_local_override``.\n\nArgs:\n request: FastAPI request object.\n _auth: Validated session \u2014 enforces authentication.\n name: Action base name (with or without ``.conf`` extension).\n\nReturns:\n :class:`~app.models.config.ActionConfig`.\n\nRaises:\n HTTPException: 404 if the action is not found in ``action.d/``.", "operationId": "get_action_api_v1_config_actions__name__get", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Action base name, e.g. ``iptables`` or ``iptables.conf``.", "title": "Name" }, "description": "Action base name, e.g. ``iptables`` or ``iptables.conf``." } ], "responses": { "200": { "description": "Action config returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ActionConfig" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Action not found in action.d/" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } }, "put": { "tags": [ "Config", "Action Config" ], "summary": "Update an action's .local override with new lifecycle command values", "description": "Update an action's ``[Definition]`` fields by writing a ``.local`` override.\n\nOnly non-``null`` fields in the request body are written. The original\n``.conf`` file is never modified.\n\nArgs:\n request: FastAPI request object.\n _auth: Validated session.\n name: Action base name (with or without ``.conf`` extension).\n body: Partial update \u2014 lifecycle commands and ``[Init]`` parameters.\n reload: When ``true``, trigger a fail2ban reload after writing.\n\nReturns:\n Updated :class:`~app.models.config.ActionConfig`.\n\nRaises:\n HTTPException: 400 if *name* contains invalid characters.\n HTTPException: 404 if the action does not exist.\n HTTPException: 500 if writing the ``.local`` file fails.", "operationId": "update_action_api_v1_config_actions__name__put", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Action base name, e.g. ``iptables`` or ``iptables.conf``.", "title": "Name" }, "description": "Action base name, e.g. ``iptables`` or ``iptables.conf``." }, { "name": "reload", "in": "query", "required": false, "schema": { "type": "boolean", "description": "Reload fail2ban after writing.", "default": false, "title": "Reload" }, "description": "Reload fail2ban after writing." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ActionUpdateRequest" } } } }, "responses": { "200": { "description": "Action updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ActionConfig" } } } }, "400": { "description": "Invalid action name" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Action not found" }, "429": { "description": "Rate limit exceeded for action update operations" }, "500": { "description": "Failed to write .local file" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } }, "delete": { "tags": [ "Config", "Action Config" ], "summary": "Delete a user-created action's .local file", "description": "Delete a user-created action's ``.local`` override file.\n\nShipped ``.conf``-only actions cannot be deleted (returns 409). When\nboth a ``.conf`` and a ``.local`` exist, only the ``.local`` is removed.\n\nArgs:\n request: FastAPI request object.\n _auth: Validated session.\n name: Action base name.\n\nRaises:\n HTTPException: 400 if *name* contains invalid characters.\n HTTPException: 404 if the action does not exist.\n HTTPException: 409 if the action is a shipped default (conf-only).\n HTTPException: 500 if deletion fails.", "operationId": "delete_action_api_v1_config_actions__name__delete", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Action base name, e.g. ``iptables`` or ``iptables.conf``.", "title": "Name" }, "description": "Action base name, e.g. ``iptables`` or ``iptables.conf``." } ], "responses": { "204": { "description": "Action deleted successfully" }, "400": { "description": "Invalid action name" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Action not found" }, "409": { "description": "Action is a shipped default (conf-only)" }, "429": { "description": "Rate limit exceeded for action delete operations" }, "500": { "description": "Failed to delete .local file" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/global": { "get": { "tags": [ "Config", "Config Misc" ], "summary": "Return global fail2ban settings", "description": "Return global fail2ban settings.\n\nIncludes log level, log target, and database configuration.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n\nReturns:\n :class:`~app.models.config.GlobalConfigResponse`.\n\nRaises:\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "get_global_config_api_v1_config_global_get", "responses": { "200": { "description": "Global config returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GlobalConfigResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "502": { "description": "fail2ban unreachable" } } }, "put": { "tags": [ "Config", "Config Misc" ], "summary": "Update global fail2ban settings", "description": "Update global fail2ban settings.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n body: Partial update \u2014 only non-None fields are written.\n\nRaises:\n HTTPException: 400 when a set command is rejected or log_target is invalid.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "update_global_config_api_v1_config_global_put", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GlobalConfigUpdate" } } }, "required": true }, "responses": { "204": { "description": "Global config updated successfully" }, "400": { "description": "Set command rejected or log_target invalid" }, "401": { "description": "Session missing, expired, or invalid" }, "429": { "description": "Rate limit exceeded for config update operations" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/reload": { "post": { "tags": [ "Config", "Config Misc" ], "summary": "Reload fail2ban to apply configuration changes", "description": "Trigger a full fail2ban reload.\n\nAll jails are stopped and restarted with the current configuration.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n\nRaises:\n HTTPException: 409 when fail2ban reports the reload failed.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "reload_fail2ban_api_v1_config_reload_post", "responses": { "204": { "description": "Fail2ban reloaded successfully" }, "401": { "description": "Session missing, expired, or invalid" }, "409": { "description": "Reload command failed in fail2ban" }, "502": { "description": "fail2ban unreachable" } } } }, "/api/v1/config/restart": { "post": { "tags": [ "Config", "Config Misc" ], "summary": "Restart the fail2ban service", "description": "Trigger a full fail2ban service restart.\n\nStops the fail2ban daemon via the Unix domain socket, then starts it\nagain using the configured ``fail2ban_start_command``. After starting,\nprobes the socket for up to 10 seconds to confirm the daemon came back\nonline.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n\nRaises:\n HTTPException: 409 when fail2ban reports the stop command failed.\n HTTPException: 502 when fail2ban is unreachable for the stop command.\n HTTPException: 503 when fail2ban does not come back online within\n 10 seconds after being started. Check the fail2ban log for\n initialisation errors. Use\n ``POST /api/config/jails/{name}/rollback``\n if a specific jail is suspect.", "operationId": "restart_fail2ban_api_v1_config_restart_post", "responses": { "204": { "description": "Fail2ban restarted successfully" }, "401": { "description": "Session missing, expired, or invalid" }, "409": { "description": "Stop command failed in fail2ban" }, "502": { "description": "fail2ban unreachable for stop command" }, "503": { "description": "fail2ban did not come back online within 10s" } } } }, "/api/v1/config/regex-test": { "post": { "tags": [ "Config", "Config Misc" ], "summary": "Test a fail regex pattern against a sample log line", "description": "Test whether a regex pattern matches a given log line.\n\nThis endpoint is entirely in-process \u2014 no fail2ban socket call is made.\nReturns the match result and any captured groups.\n\nArgs:\n _auth: Validated session.\n body: Sample log line and regex pattern.\n\nReturns:\n :class:`~app.models.config.RegexTestResponse` with match result and\n groups.", "operationId": "regex_test_api_v1_config_regex_test_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RegexTestRequest" } } }, "required": true }, "responses": { "200": { "description": "Regex test result", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RegexTestResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "422": { "description": "Invalid regex pattern" } } } }, "/api/v1/config/preview-log": { "post": { "tags": [ "Config", "Config Misc" ], "summary": "Preview log file lines against a regex pattern", "description": "Read the last N lines of a log file and test a regex against each one.\n\nReturns each line with a flag indicating whether the regex matched, and\nthe captured groups for matching lines. The log file is read from the\nserver's local filesystem.\n\nArgs:\n _auth: Validated session.\n body: Log file path, regex pattern, and number of lines to read.\n\nReturns:\n :class:`~app.models.config.LogPreviewResponse` with per-line results.", "operationId": "preview_log_api_v1_config_preview_log_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LogPreviewRequest" } } }, "required": true }, "responses": { "200": { "description": "Log preview result", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LogPreviewResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "422": { "description": "Invalid regex pattern" } } } }, "/api/v1/config/map-color-thresholds": { "get": { "tags": [ "Config", "Config Misc" ], "summary": "Get map color threshold configuration", "description": "Return the configured map color thresholds.\n\nArgs:\n _request: FastAPI request object.\n _auth: Validated session.\n settings_ctx: Settings service context containing db and repository.\n\nReturns:\n :class:`~app.models.config.MapColorThresholdsResponse` with\n current thresholds.", "operationId": "get_map_color_thresholds_api_v1_config_map_color_thresholds_get", "responses": { "200": { "description": "Color thresholds returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MapColorThresholdsResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" } } }, "put": { "tags": [ "Config", "Config Misc" ], "summary": "Update map color threshold configuration", "description": "Update the map color threshold configuration.\n\nArgs:\n _request: FastAPI request object.\n _auth: Validated session.\n settings_ctx: Settings service context containing db and repository.\n body: New threshold values.\n\nReturns:\n :class:`~app.models.config.MapColorThresholdsResponse` with\n updated thresholds.\n\nRaises:\n HTTPException: 400 if validation fails (thresholds not\n properly ordered).", "operationId": "update_map_color_thresholds_api_v1_config_map_color_thresholds_put", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MapColorThresholdsUpdate" } } }, "required": true }, "responses": { "200": { "description": "Color thresholds updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MapColorThresholdsResponse" } } } }, "400": { "description": "Validation error (thresholds not properly ordered)" }, "401": { "description": "Session missing, expired, or invalid" }, "429": { "description": "Rate limit exceeded for config update operations" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/fail2ban-log": { "get": { "tags": [ "Config", "Config Misc" ], "summary": "Read the tail of the fail2ban daemon log file", "description": "Return the tail of the fail2ban daemon log file.\n\nQueries the fail2ban socket for the current log target and log level,\nreads the last *lines* entries from the file, and optionally filters\nthem by *filter*. Only file-based log targets are supported.\n\nArgs:\n request: Incoming request.\n _auth: Validated session \u2014 enforces authentication.\n lines: Number of tail lines to return (1\u20132000, default 200).\n filter: Optional plain-text substring \u2014 only matching lines returned.\n\nReturns:\n :class:`~app.models.config.Fail2BanLogResponse`.\n\nRaises:\n HTTPException: 400 when the log target is not a file or path is outside\n the allowed directory.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "get_fail2ban_log_api_v1_config_fail2ban_log_get", "parameters": [ { "name": "lines", "in": "query", "required": false, "schema": { "type": "integer", "maximum": 2000, "minimum": 1, "description": "Number of lines to return from the tail.", "default": 200, "title": "Lines" }, "description": "Number of lines to return from the tail." }, { "name": "filter", "in": "query", "required": false, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "description": "Plain-text substring filter; only matching lines are returned.", "title": "Filter" }, "description": "Plain-text substring filter; only matching lines are returned." } ], "responses": { "200": { "description": "Log file lines returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Fail2BanLogResponse" } } } }, "400": { "description": "Log target not a file or path outside allowed directory" }, "401": { "description": "Session missing, expired, or invalid" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/service-status": { "get": { "tags": [ "Config", "Config Misc" ], "summary": "Return fail2ban service health status with log configuration", "description": "Return fail2ban service health and current log configuration.\n\nProbes the fail2ban daemon to determine online/offline state, then\naugments the result with the current log level and log target values.\n\nArgs:\n request: Incoming request.\n _auth: Validated session \u2014 enforces authentication.\n\nReturns:\n :class:`~app.models.config.ServiceStatusResponse`.\n\nRaises:\n HTTPException: 502 when fail2ban is unreachable (the service itself\n handles this gracefully and returns ``online=False``).", "operationId": "get_service_status_api_v1_config_service_status_get", "responses": { "200": { "description": "Service status returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ServiceStatusResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "502": { "description": "fail2ban unreachable" } } } }, "/api/v1/config/security-headers": { "get": { "tags": [ "Config", "Config Misc" ], "summary": "Return security-relevant header configuration", "description": "Return the header name and value used for CSRF protection.\n\nThis endpoint allows the frontend to discover the required CSRF header\nname and value at runtime rather than hard-coding them. The response\nis derived from the same constants used by the backend CSRF middleware,\nensuring a single source of truth.\n\nArgs:\n request: Incoming request.\n _auth: Validated session \u2014 enforces authentication.\n\nReturns:\n :class:`~app.models.config.SecurityHeadersResponse` with\n ``csrf_header_name`` and ``csrf_header_value``.", "operationId": "get_security_headers_api_v1_config_security_headers_get", "responses": { "200": { "description": "Security header names and values returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SecurityHeadersResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" } } } }, "/api/v1/config/jail-files": { "get": { "tags": [ "Config" ], "summary": "List all jail config files", "description": "Return metadata for every ``.conf`` and ``.local`` file in ``jail.d/``.\n\nThe ``enabled`` field reflects the value of the ``enabled`` key inside the\nfile (defaulting to ``true`` when the key is absent).\n\nArgs:\n config_dir: Config directory path injected from application settings.\n _auth: Validated session \u2014 enforces authentication.\n\nReturns:\n :class:`~app.models.file_config.JailConfigFilesResponse`.", "operationId": "list_jail_config_files_api_v1_config_jail_files_get", "responses": { "200": { "description": "Jail config files returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailConfigFilesResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "503": { "description": "Config directory unavailable" } } }, "post": { "tags": [ "Config" ], "summary": "Create a new jail.d config file", "description": "Create a new ``.conf`` file in ``jail.d/``.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n body: :class:`~app.models.file_config.ConfFileCreateRequest` with name and content.\n\nReturns:\n :class:`~app.models.file_config.ConfFileContent` with the created file metadata.\n\nRaises:\n HTTPException: 400 if the name is unsafe or the content exceeds the size limit.\n HTTPException: 409 if a file with that name already exists.\n HTTPException: 503 if the config directory is unavailable.", "operationId": "create_jail_config_file_api_v1_config_jail_files_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConfFileCreateRequest" } } }, "required": true }, "responses": { "201": { "description": "File created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConfFileContent" } } } }, "400": { "description": "Name unsafe or content exceeds size limit" }, "401": { "description": "Session missing, expired, or invalid" }, "409": { "description": "File with that name already exists" }, "503": { "description": "Config directory unavailable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/jail-files/{filename}": { "get": { "tags": [ "Config" ], "summary": "Return a single jail config file with its content", "description": "Return the metadata and raw content of one jail config file.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n filename: Filename including extension (e.g. ``sshd.conf``).\n\nReturns:\n :class:`~app.models.file_config.JailConfigFileContent`.\n\nRaises:\n HTTPException: 400 if *filename* is unsafe.\n HTTPException: 404 if the file does not exist.\n HTTPException: 503 if the config directory is unavailable.", "operationId": "get_jail_config_file_api_v1_config_jail_files__filename__get", "parameters": [ { "name": "filename", "in": "path", "required": true, "schema": { "type": "string", "description": "Config filename including extension (e.g. ``sshd.conf``).", "title": "Filename" }, "description": "Config filename including extension (e.g. ``sshd.conf``)." } ], "responses": { "200": { "description": "Jail config file returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailConfigFileContent" } } } }, "400": { "description": "Filename unsafe" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "File not found" }, "503": { "description": "Config directory unavailable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } }, "put": { "tags": [ "Config" ], "summary": "Overwrite a jail.d config file with new raw content", "description": "Overwrite the raw content of an existing jail.d config file.\n\nThe change is written directly to disk. You must reload fail2ban\n(``POST /api/config/reload``) separately for the change to take effect.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n filename: Filename of the jail config file (e.g. ``sshd.conf``).\n body: New raw file content.\n\nRaises:\n HTTPException: 400 if *filename* is unsafe or content is invalid.\n HTTPException: 404 if the file does not exist.\n HTTPException: 503 if the config directory is unavailable.", "operationId": "write_jail_config_file_api_v1_config_jail_files__filename__put", "parameters": [ { "name": "filename", "in": "path", "required": true, "schema": { "type": "string", "description": "Config filename including extension (e.g. ``sshd.conf``).", "title": "Filename" }, "description": "Config filename including extension (e.g. ``sshd.conf``)." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConfFileUpdateRequest" } } } }, "responses": { "204": { "description": "File overwritten successfully" }, "400": { "description": "Filename unsafe or content invalid" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "File not found" }, "503": { "description": "Config directory unavailable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/jail-files/{filename}/enabled": { "put": { "tags": [ "Config" ], "summary": "Enable or disable a jail configuration file", "description": "Set the ``enabled = true/false`` key inside a jail config file.\n\nThe change modifies the file on disk. You must reload fail2ban\n(``POST /api/config/reload``) separately for the change to take effect.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n filename: Filename of the jail config file (e.g. ``sshd.conf``).\n body: New enabled state.\n\nRaises:\n HTTPException: 400 if *filename* is unsafe or the operation fails.\n HTTPException: 404 if the file does not exist.\n HTTPException: 503 if the config directory is unavailable.", "operationId": "set_jail_config_file_enabled_api_v1_config_jail_files__filename__enabled_put", "parameters": [ { "name": "filename", "in": "path", "required": true, "schema": { "type": "string", "description": "Config filename including extension (e.g. ``sshd.conf``).", "title": "Filename" }, "description": "Config filename including extension (e.g. ``sshd.conf``)." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailConfigFileEnabledUpdate" } } } }, "responses": { "204": { "description": "Enabled state updated successfully" }, "400": { "description": "Filename unsafe or operation failed" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "File not found" }, "503": { "description": "Config directory unavailable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/filters/{name}/raw": { "get": { "tags": [ "Config" ], "summary": "Return a filter definition file's raw content", "description": "Return the raw content of a filter definition file.\n\nThis endpoint provides direct access to the file bytes for the raw\nconfig editor. For structured parsing with active/inactive status use\n``GET /api/config/filters/{name}`` (served by the config router).\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n name: Base name with or without extension (e.g. ``sshd`` or ``sshd.conf``).\n\nReturns:\n :class:`~app.models.file_config.ConfFileContent`.\n\nRaises:\n HTTPException: 400 if *name* is unsafe.\n HTTPException: 404 if the file does not exist.\n HTTPException: 503 if the config directory is unavailable.", "operationId": "get_filter_file_raw_api_v1_config_filters__name__raw_get", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Base name with or without extension (e.g. ``sshd`` or ``sshd.conf``).", "title": "Name" }, "description": "Base name with or without extension (e.g. ``sshd`` or ``sshd.conf``)." } ], "responses": { "200": { "description": "Filter file returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConfFileContent" } } } }, "400": { "description": "Name unsafe" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "File not found" }, "503": { "description": "Config directory unavailable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } }, "put": { "tags": [ "Config" ], "summary": "Update a filter definition file (raw content)", "description": "Overwrite the content of an existing filter definition file.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n name: Base name with or without extension.\n body: New file content.\n\nRaises:\n HTTPException: 400 if *name* is unsafe or content exceeds the size limit.\n HTTPException: 404 if the file does not exist.\n HTTPException: 503 if the config directory is unavailable.", "operationId": "write_filter_file_api_v1_config_filters__name__raw_put", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Base name with or without extension (e.g. ``sshd`` or ``sshd.conf``).", "title": "Name" }, "description": "Base name with or without extension (e.g. ``sshd`` or ``sshd.conf``)." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConfFileUpdateRequest" } } } }, "responses": { "204": { "description": "Filter file updated successfully" }, "400": { "description": "Name unsafe or content exceeds size limit" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "File not found" }, "503": { "description": "Config directory unavailable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/filters/raw": { "post": { "tags": [ "Config" ], "summary": "Create a new filter definition file (raw content)", "description": "Create a new ``.conf`` file in ``filter.d/``.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n body: Name and initial content for the new file.\n\nReturns:\n The created :class:`~app.models.file_config.ConfFileContent`.\n\nRaises:\n HTTPException: 400 if *name* is invalid or content exceeds limit.\n HTTPException: 409 if a file with that name already exists.\n HTTPException: 503 if the config directory is unavailable.", "operationId": "create_filter_file_api_v1_config_filters_raw_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConfFileCreateRequest" } } }, "required": true }, "responses": { "201": { "description": "Filter file created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConfFileContent" } } } }, "400": { "description": "Name invalid or content exceeds limit" }, "401": { "description": "Session missing, expired, or invalid" }, "409": { "description": "File with that name already exists" }, "503": { "description": "Config directory unavailable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/actions/{name}/raw": { "get": { "tags": [ "Config" ], "summary": "Return an action definition file with its content", "description": "Return the content of an action definition file.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n name: Base name with or without extension.\n\nReturns:\n :class:`~app.models.file_config.ConfFileContent`.\n\nRaises:\n HTTPException: 400 if *name* is unsafe.\n HTTPException: 404 if the file does not exist.\n HTTPException: 503 if the config directory is unavailable.", "operationId": "get_action_file_api_v1_config_actions__name__raw_get", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Base name with or without extension (e.g. ``sshd`` or ``sshd.conf``).", "title": "Name" }, "description": "Base name with or without extension (e.g. ``sshd`` or ``sshd.conf``)." } ], "responses": { "200": { "description": "Action file returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConfFileContent" } } } }, "400": { "description": "Name unsafe" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "File not found" }, "503": { "description": "Config directory unavailable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } }, "put": { "tags": [ "Config" ], "summary": "Update an action definition file", "description": "Overwrite the content of an existing action definition file.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n name: Base name with or without extension.\n body: New file content.\n\nRaises:\n HTTPException: 400 if *name* is unsafe or content exceeds the size limit.\n HTTPException: 404 if the file does not exist.\n HTTPException: 503 if the config directory is unavailable.", "operationId": "write_action_file_api_v1_config_actions__name__raw_put", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Base name with or without extension (e.g. ``sshd`` or ``sshd.conf``).", "title": "Name" }, "description": "Base name with or without extension (e.g. ``sshd`` or ``sshd.conf``)." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConfFileUpdateRequest" } } } }, "responses": { "204": { "description": "Action file updated successfully" }, "400": { "description": "Name unsafe or content exceeds size limit" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "File not found" }, "503": { "description": "Config directory unavailable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/filters/{name}/parsed": { "get": { "tags": [ "Config" ], "summary": "Return a filter file parsed into a structured model", "description": "Parse a filter definition file and return its structured fields.\n\nThe file is read from ``filter.d/``, parsed as fail2ban INI format, and\nreturned as a :class:`~app.models.config.FilterConfig` JSON object. This\nis the input model for the form-based filter editor (Task 2.3).\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n name: Base name (e.g. ``sshd`` or ``sshd.conf``).\n\nReturns:\n :class:`~app.models.config.FilterConfig`.\n\nRaises:\n HTTPException: 400 if *name* is unsafe.\n HTTPException: 404 if the file does not exist.\n HTTPException: 503 if the config directory is unavailable.", "operationId": "get_parsed_filter_api_v1_config_filters__name__parsed_get", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Base name with or without extension (e.g. ``sshd`` or ``sshd.conf``).", "title": "Name" }, "description": "Base name with or without extension (e.g. ``sshd`` or ``sshd.conf``)." } ], "responses": { "200": { "description": "Filter config returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FilterConfig" } } } }, "400": { "description": "Name unsafe" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Filter file not found" }, "503": { "description": "Config directory unavailable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } }, "put": { "tags": [ "Config" ], "summary": "Update a filter file from a structured model", "description": "Apply a partial structured update to a filter definition file.\n\nFields set to ``null`` in the request body are left unchanged. The file is\nre-serialized to fail2ban INI format after merging.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n name: Base name of the filter to update.\n body: Partial :class:`~app.models.config.FilterConfigUpdate`.\n\nRaises:\n HTTPException: 400 if *name* is unsafe or content exceeds the size limit.\n HTTPException: 404 if the file does not exist.\n HTTPException: 503 if the config directory is unavailable.", "operationId": "update_parsed_filter_api_v1_config_filters__name__parsed_put", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Base name with or without extension (e.g. ``sshd`` or ``sshd.conf``).", "title": "Name" }, "description": "Base name with or without extension (e.g. ``sshd`` or ``sshd.conf``)." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FilterConfigUpdate" } } } }, "responses": { "204": { "description": "Filter file updated successfully" }, "400": { "description": "Name unsafe or content exceeds size limit" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Filter file not found" }, "503": { "description": "Config directory unavailable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/actions/{name}/parsed": { "get": { "tags": [ "Config" ], "summary": "Return an action file parsed into a structured model", "description": "Parse an action definition file and return its structured fields.\n\nThe file is read from ``action.d/``, parsed as fail2ban INI format, and\nreturned as a :class:`~app.models.config.ActionConfig` JSON object. This\nis the input model for the form-based action editor (Task 3.3).\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n name: Base name (e.g. ``iptables`` or ``iptables.conf``).\n\nReturns:\n :class:`~app.models.config.ActionConfig`.\n\nRaises:\n HTTPException: 400 if *name* is unsafe.\n HTTPException: 404 if the file does not exist.\n HTTPException: 503 if the config directory is unavailable.", "operationId": "get_parsed_action_api_v1_config_actions__name__parsed_get", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Base name with or without extension (e.g. ``sshd`` or ``sshd.conf``).", "title": "Name" }, "description": "Base name with or without extension (e.g. ``sshd`` or ``sshd.conf``)." } ], "responses": { "200": { "description": "Action config returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ActionConfig" } } } }, "400": { "description": "Name unsafe" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Action file not found" }, "503": { "description": "Config directory unavailable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } }, "put": { "tags": [ "Config" ], "summary": "Update an action file from a structured model", "description": "Apply a partial structured update to an action definition file.\n\nFields set to ``null`` in the request body are left unchanged. The file is\nre-serialized to fail2ban INI format after merging.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n name: Base name of the action to update.\n body: Partial :class:`~app.models.config.ActionConfigUpdate`.\n\nRaises:\n HTTPException: 400 if *name* is unsafe or content exceeds the size limit.\n HTTPException: 404 if the file does not exist.\n HTTPException: 503 if the config directory is unavailable.", "operationId": "update_parsed_action_api_v1_config_actions__name__parsed_put", "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string", "description": "Base name with or without extension (e.g. ``sshd`` or ``sshd.conf``).", "title": "Name" }, "description": "Base name with or without extension (e.g. ``sshd`` or ``sshd.conf``)." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ActionConfigUpdate" } } } }, "responses": { "204": { "description": "Action file updated successfully" }, "400": { "description": "Name unsafe or content exceeds size limit" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Action file not found" }, "503": { "description": "Config directory unavailable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/config/jail-files/{filename}/parsed": { "get": { "tags": [ "Config" ], "summary": "Return a jail.d file parsed into a structured model", "description": "Parse a jail.d config file and return its structured fields.\n\nThe file is read from ``jail.d/``, parsed as fail2ban INI format, and\nreturned as a :class:`~app.models.config.JailFileConfig` JSON object. This\nis the input model for the form-based jail file editor (Task 6.2).\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n filename: Filename including extension (e.g. ``sshd.conf``).\n\nReturns:\n :class:`~app.models.config.JailFileConfig`.\n\nRaises:\n HTTPException: 400 if *filename* is unsafe.\n HTTPException: 404 if the file does not exist.\n HTTPException: 503 if the config directory is unavailable.", "operationId": "get_parsed_jail_file_api_v1_config_jail_files__filename__parsed_get", "parameters": [ { "name": "filename", "in": "path", "required": true, "schema": { "type": "string", "description": "Base name with or without extension (e.g. ``sshd`` or ``sshd.conf``).", "title": "Filename" }, "description": "Base name with or without extension (e.g. ``sshd`` or ``sshd.conf``)." } ], "responses": { "200": { "description": "Jail file config returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailFileConfig" } } } }, "400": { "description": "Filename unsafe" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail file not found" }, "503": { "description": "Config directory unavailable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } }, "put": { "tags": [ "Config" ], "summary": "Update a jail.d file from a structured model", "description": "Apply a partial structured update to a jail.d config file.\n\nFields set to ``null`` in the request body are left unchanged. The file is\nre-serialized to fail2ban INI format after merging.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n filename: Filename including extension (e.g. ``sshd.conf``).\n body: Partial :class:`~app.models.config.JailFileConfigUpdate`.\n\nRaises:\n HTTPException: 400 if *filename* is unsafe or content exceeds size limit.\n HTTPException: 404 if the file does not exist.\n HTTPException: 503 if the config directory is unavailable.", "operationId": "update_parsed_jail_file_api_v1_config_jail_files__filename__parsed_put", "parameters": [ { "name": "filename", "in": "path", "required": true, "schema": { "type": "string", "description": "Base name with or without extension (e.g. ``sshd`` or ``sshd.conf``).", "title": "Filename" }, "description": "Base name with or without extension (e.g. ``sshd`` or ``sshd.conf``)." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JailFileConfigUpdate" } } } }, "responses": { "204": { "description": "Jail file updated successfully" }, "400": { "description": "Filename unsafe or content exceeds size limit" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Jail file not found" }, "503": { "description": "Config directory unavailable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/server/settings": { "get": { "tags": [ "Server" ], "summary": "Return fail2ban server-level settings", "description": "Return the current fail2ban server-level settings.\n\nIncludes log level, log target, syslog socket, database file path,\ndatabase purge age, and maximum stored matches per record.\n\nArgs:\n request: Incoming request (used to access ``app.state``).\n _auth: Validated session \u2014 enforces authentication.\n\nReturns:\n :class:`~app.models.server.ServerSettingsResponse`.\n\nRaises:\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "get_server_settings_api_v1_server_settings_get", "responses": { "200": { "description": "Server settings returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ServerSettingsResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "502": { "description": "fail2ban unreachable" } } }, "put": { "tags": [ "Server" ], "summary": "Update fail2ban server-level settings", "description": "Update fail2ban server-level settings.\n\nOnly non-None fields in the request body are written. Changes take\neffect immediately without a daemon restart.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n body: Partial settings update.\n\nRaises:\n HTTPException: 400 when a set command is rejected by fail2ban.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "update_server_settings_api_v1_server_settings_put", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ServerSettingsUpdate" } } }, "required": true }, "responses": { "204": { "description": "Settings updated successfully" }, "400": { "description": "Set command rejected by fail2ban" }, "401": { "description": "Session missing, expired, or invalid" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/server/flush-logs": { "post": { "tags": [ "Server" ], "summary": "Flush and re-open fail2ban log files", "description": "Flush and re-open fail2ban log files.\n\nUseful after log rotation so the daemon writes to the newly created\nlog file rather than continuing to append to the rotated one.\n\nArgs:\n request: Incoming request.\n _auth: Validated session.\n\nReturns:\n :class:`~app.models.response.FlushLogsResponse` with the result from fail2ban.\n\nRaises:\n HTTPException: 400 when the command is rejected.\n HTTPException: 502 when fail2ban is unreachable.", "operationId": "flush_logs_api_v1_server_flush_logs_post", "responses": { "200": { "description": "Logs flushed successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FlushLogsResponse" } } } }, "400": { "description": "Command rejected by fail2ban" }, "401": { "description": "Session missing, expired, or invalid" }, "502": { "description": "fail2ban unreachable" } } } }, "/api/v1/history": { "get": { "tags": [ "History" ], "summary": "Return a paginated list of historical bans", "description": "Return a paginated list of historical bans with optional filters.\n\nQueries the fail2ban database for all ban records, applying the requested\nfilters. Results are ordered newest-first and enriched with geolocation.\n\nArgs:\n request: The incoming request (used to access ``app.state``).\n _auth: Validated session \u2014 enforces authentication.\n history_ctx: History service context containing db and repositories.\n socket_path: Path to fail2ban Unix domain socket.\n http_session: Shared HTTP session for geolocation.\n fail2ban_metadata_service: Fail2Ban metadata service.\n range: Optional time-range preset. ``None`` means all-time.\n jail: Optional jail name filter (exact match).\n ip: Optional IP prefix filter (prefix match).\n page: 1-based page number.\n page_size: Items per page (1\u2013500).\n\nReturns:\n :class:`~app.models.history.HistoryListResponse` with paginated items\n and the total matching count.", "operationId": "get_history_api_v1_history_get", "parameters": [ { "name": "range", "in": "query", "required": false, "schema": { "anyOf": [ { "enum": [ "24h", "7d", "30d", "365d" ], "type": "string" }, { "type": "null" } ], "description": "Optional time-range filter. Omit for all-time.", "title": "Range" }, "description": "Optional time-range filter. Omit for all-time." }, { "name": "jail", "in": "query", "required": false, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "description": "Restrict results to this jail name.", "title": "Jail" }, "description": "Restrict results to this jail name." }, { "name": "ip", "in": "query", "required": false, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "description": "Restrict results to IPs matching this prefix.", "title": "Ip" }, "description": "Restrict results to IPs matching this prefix." }, { "name": "origin", "in": "query", "required": false, "schema": { "anyOf": [ { "enum": [ "blocklist", "selfblock" ], "type": "string" }, { "type": "null" } ], "description": "Filter by ban origin: 'blocklist' or 'selfblock'. Omit for all.", "title": "Origin" }, "description": "Filter by ban origin: 'blocklist' or 'selfblock'. Omit for all." }, { "name": "source", "in": "query", "required": false, "schema": { "enum": [ "fail2ban", "archive" ], "type": "string", "description": "Data source: 'fail2ban' or 'archive'.", "default": "fail2ban", "title": "Source" }, "description": "Data source: 'fail2ban' or 'archive'." }, { "name": "page", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1, "description": "1-based page number.", "default": 1, "title": "Page" }, "description": "1-based page number." }, { "name": "page_size", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1, "description": "Items per page.", "default": 100, "title": "Page Size" }, "description": "Items per page." } ], "responses": { "200": { "description": "History list returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HistoryListResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/history/archive": { "get": { "tags": [ "History" ], "summary": "Return a paginated list of archived historical bans", "operationId": "get_history_archive_api_v1_history_archive_get", "parameters": [ { "name": "range", "in": "query", "required": false, "schema": { "anyOf": [ { "enum": [ "24h", "7d", "30d", "365d" ], "type": "string" }, { "type": "null" } ], "description": "Optional time-range filter. Omit for all-time.", "title": "Range" }, "description": "Optional time-range filter. Omit for all-time." }, { "name": "jail", "in": "query", "required": false, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "description": "Restrict results to this jail name.", "title": "Jail" }, "description": "Restrict results to this jail name." }, { "name": "ip", "in": "query", "required": false, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "description": "Restrict results to IPs matching this prefix.", "title": "Ip" }, "description": "Restrict results to IPs matching this prefix." }, { "name": "page", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1, "description": "1-based page number.", "default": 1, "title": "Page" }, "description": "1-based page number." }, { "name": "page_size", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1, "description": "Items per page.", "default": 100, "title": "Page Size" }, "description": "Items per page." } ], "responses": { "200": { "description": "Archived history list returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HistoryListResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/history/{ip}": { "get": { "tags": [ "History" ], "summary": "Return the full ban history for a single IP address", "description": "Return the complete historical record for a single IP address.\n\nFetches all ban events for the given IP from the fail2ban database and\naggregates them into a timeline. Returns ``404`` if the IP has no\nrecorded history.\n\nArgs:\n request: The incoming request.\n _auth: Validated session dependency.\n ip: The IP address to look up.\n\nReturns:\n :class:`~app.models.history.IpDetailResponse` with aggregated totals\n and a full ban timeline.\n\nRaises:\n HTTPException: 404 if the IP has no history in the database.", "operationId": "get_ip_history_api_v1_history__ip__get", "parameters": [ { "name": "ip", "in": "path", "required": true, "schema": { "type": "string", "title": "Ip" } } ], "responses": { "200": { "description": "IP history detail returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/IpDetailResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "No history found for this IP" }, "502": { "description": "fail2ban unreachable" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/blocklists": { "get": { "tags": [ "Blocklists" ], "summary": "List all blocklist sources", "description": "Return all configured blocklist source definitions.\n\nArgs:\n blocklist_ctx: Blocklist service context containing db and repositories.\n _auth: Validated session \u2014 enforces authentication.\n\nReturns:\n :class:`~app.models.blocklist.BlocklistListResponse` with all sources.", "operationId": "list_blocklists_api_v1_blocklists_get", "responses": { "200": { "description": "Blocklist sources returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BlocklistListResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" } } }, "post": { "tags": [ "Blocklists" ], "summary": "Add a new blocklist source", "description": "Create a new blocklist source definition.\n\nArgs:\n payload: New source data (name, url, enabled).\n blocklist_ctx: Blocklist service context containing db and repositories.\n _auth: Validated session \u2014 enforces authentication.\n\nReturns:\n The newly created :class:`~app.models.blocklist.BlocklistSource`.\n\nRaises:\n HTTPException: 400 if URL validation fails.", "operationId": "create_blocklist_api_v1_blocklists_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BlocklistSourceCreate" } } }, "required": true }, "responses": { "201": { "description": "Blocklist source created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BlocklistSource" } } } }, "400": { "description": "URL validation failed" }, "401": { "description": "Session missing, expired, or invalid" }, "409": { "description": "A blocklist source with this URL already exists" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/blocklists/import": { "post": { "tags": [ "Blocklists" ], "summary": "Trigger a manual blocklist import", "description": "Download and apply all enabled blocklist sources immediately.\n\nArgs:\n http_session: Shared HTTP session (injected).\n blocklist_ctx: Blocklist service context containing db and repositories.\n _auth: Validated session \u2014 enforces authentication.\n socket_path: Path to fail2ban Unix domain socket.\n geo_cache: Geolocation cache instance.\n\nReturns:\n :class:`~app.models.blocklist.ImportRunResult` with per-source\n results and aggregated counters.", "operationId": "run_import_now_api_v1_blocklists_import_post", "responses": { "200": { "description": "Import completed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImportRunResult" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "429": { "description": "Rate limit exceeded for blocklist import" } } } }, "/api/v1/blocklists/schedule": { "get": { "tags": [ "Blocklists" ], "summary": "Get the current import schedule", "description": "Return the current schedule configuration and runtime metadata.\n\nThe ``next_run_at`` field is read from APScheduler if the job is active.\n\nArgs:\n blocklist_ctx: Blocklist service context containing db and repositories.\n _auth: Validated session \u2014 enforces authentication.\n scheduler: APScheduler instance.\n\nReturns:\n :class:`~app.models.blocklist.ScheduleInfo` with config and run\n times.", "operationId": "get_schedule_api_v1_blocklists_schedule_get", "responses": { "200": { "description": "Schedule info returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScheduleInfo" } } } }, "401": { "description": "Session missing, expired, or invalid" } } }, "put": { "tags": [ "Blocklists" ], "summary": "Update the import schedule", "description": "Persist a new schedule configuration and reschedule the import job.\n\nArgs:\n payload: New :class:`~app.models.blocklist.ScheduleConfig`.\n blocklist_ctx: Blocklist service context containing db and repositories.\n _auth: Validated session \u2014 enforces authentication.\n scheduler: Shared APScheduler instance (injected).\n http_session: Shared HTTP session used by the scheduler job.\n settings: Current application settings used by the scheduler job.\n\nReturns:\n Updated :class:`~app.models.blocklist.ScheduleInfo`.", "operationId": "update_schedule_api_v1_blocklists_schedule_put", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScheduleConfig" } } }, "required": true }, "responses": { "200": { "description": "Schedule updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScheduleInfo" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/blocklists/log": { "get": { "tags": [ "Blocklists" ], "summary": "Get the paginated import log", "description": "Return a paginated log of all import runs.\n\nArgs:\n blocklist_ctx: Blocklist service context containing db and repositories.\n _auth: Validated session \u2014 enforces authentication.\n source_id: Optional filter \u2014 only show logs for this source.\n page: 1-based page number.\n page_size: Items per page.\n\nReturns:\n :class:`~app.models.blocklist.ImportLogListResponse`.", "operationId": "get_import_log_api_v1_blocklists_log_get", "parameters": [ { "name": "source_id", "in": "query", "required": false, "schema": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "description": "Filter by source id", "title": "Source Id" }, "description": "Filter by source id" }, { "name": "page", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1, "description": "1-based page number.", "default": 1, "title": "Page" }, "description": "1-based page number." }, { "name": "page_size", "in": "query", "required": false, "schema": { "type": "integer", "maximum": 500, "minimum": 1, "description": "Items per page (max 500).", "default": 100, "title": "Page Size" }, "description": "Items per page (max 500)." } ], "responses": { "200": { "description": "Import log returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImportLogListResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/blocklists/{source_id}": { "get": { "tags": [ "Blocklists" ], "summary": "Get a single blocklist source", "description": "Return a single blocklist source by id.\n\nArgs:\n source_id: Primary key of the source.\n blocklist_ctx: Blocklist service context containing db and repositories.\n _auth: Validated session \u2014 enforces authentication.\n\nRaises:\n HTTPException: 404 if the source does not exist.", "operationId": "get_blocklist_api_v1_blocklists__source_id__get", "parameters": [ { "name": "source_id", "in": "path", "required": true, "schema": { "type": "integer", "title": "Source Id" } } ], "responses": { "200": { "description": "Blocklist source returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BlocklistSource" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Blocklist source not found" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } }, "put": { "tags": [ "Blocklists" ], "summary": "Update a blocklist source", "description": "Update one or more fields on a blocklist source.\n\nArgs:\n source_id: Primary key of the source to update.\n payload: Fields to update (all optional).\n blocklist_ctx: Blocklist service context containing db and repositories.\n _auth: Validated session \u2014 enforces authentication.\n\nRaises:\n HTTPException: 400 if URL validation fails.\n HTTPException: 404 if the source does not exist.", "operationId": "update_blocklist_api_v1_blocklists__source_id__put", "parameters": [ { "name": "source_id", "in": "path", "required": true, "schema": { "type": "integer", "title": "Source Id" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BlocklistSourceUpdate" } } } }, "responses": { "200": { "description": "Blocklist source updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BlocklistSource" } } } }, "400": { "description": "URL validation failed" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Blocklist source not found" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } }, "delete": { "tags": [ "Blocklists" ], "summary": "Delete a blocklist source", "description": "Delete a blocklist source by id.\n\nArgs:\n source_id: Primary key of the source to remove.\n blocklist_ctx: Blocklist service context containing db and repositories.\n _auth: Validated session \u2014 enforces authentication.\n\nRaises:\n HTTPException: 404 if the source does not exist.", "operationId": "delete_blocklist_api_v1_blocklists__source_id__delete", "parameters": [ { "name": "source_id", "in": "path", "required": true, "schema": { "type": "integer", "title": "Source Id" } } ], "responses": { "204": { "description": "Blocklist source deleted successfully" }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Blocklist source not found" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/api/v1/blocklists/{source_id}/preview": { "get": { "tags": [ "Blocklists" ], "summary": "Preview the contents of a blocklist source", "description": "Download and preview a sample of a blocklist source.\n\nReturns the first :data:`~app.services.blocklist_service._PREVIEW_LINES`\nvalid IP entries together with validation statistics.\n\nArgs:\n source_id: Primary key of the source to preview.\n http_session: Shared HTTP session for downloading.\n blocklist_ctx: Blocklist service context containing db and repositories.\n _auth: Validated session \u2014 enforces authentication.\n\nRaises:\n HTTPException: 404 if the source does not exist.\n HTTPException: 502 if the URL cannot be reached.", "operationId": "preview_blocklist_api_v1_blocklists__source_id__preview_get", "parameters": [ { "name": "source_id", "in": "path", "required": true, "schema": { "type": "integer", "title": "Source Id" } } ], "responses": { "200": { "description": "Blocklist preview returned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PreviewResponse" } } } }, "401": { "description": "Session missing, expired, or invalid" }, "404": { "description": "Blocklist source not found" }, "502": { "description": "URL could not be reached" }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } } }, "components": { "schemas": { "ActionConfig": { "properties": { "name": { "type": "string", "title": "Name", "description": "Action base name, e.g. ``iptables``." }, "filename": { "type": "string", "title": "Filename", "description": "Actual filename, e.g. ``iptables.conf``." }, "before": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Before" }, "after": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "After" }, "actionstart": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actionstart", "description": "Executed at jail start or first ban." }, "actionstop": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actionstop", "description": "Executed at jail stop." }, "actioncheck": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actioncheck", "description": "Executed before each ban." }, "actionban": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actionban", "description": "Executed to ban an IP. Tags: ````, ````, ````." }, "actionunban": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actionunban", "description": "Executed to unban an IP." }, "actionflush": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actionflush", "description": "Executed to flush all bans on shutdown." }, "definition_vars": { "additionalProperties": { "type": "string" }, "type": "object", "title": "Definition Vars", "description": "Additional ``[Definition]`` variables." }, "init_vars": { "additionalProperties": { "type": "string" }, "type": "object", "title": "Init Vars", "description": "Runtime parameters that can be overridden per jail." }, "active": { "type": "boolean", "title": "Active", "description": "``True`` when this action is referenced by at least one currently enabled (running) jail.", "default": false }, "used_by_jails": { "items": { "type": "string" }, "type": "array", "title": "Used By Jails", "description": "Names of currently enabled jails that reference this action. Empty when ``active`` is ``False``." }, "source_file": { "type": "string", "title": "Source File", "description": "Absolute path to the ``.conf`` source file for this action.", "default": "" }, "has_local_override": { "type": "boolean", "title": "Has Local Override", "description": "``True`` when a ``.local`` override file exists alongside the base ``.conf`` file.", "default": false } }, "type": "object", "required": [ "name", "filename" ], "title": "ActionConfig", "description": "Structured representation of an ``action.d/*.conf`` file." }, "ActionConfigUpdate": { "properties": { "before": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Before" }, "after": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "After" }, "actionstart": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actionstart" }, "actionstop": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actionstop" }, "actioncheck": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actioncheck" }, "actionban": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actionban" }, "actionunban": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actionunban" }, "actionflush": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actionflush" }, "definition_vars": { "anyOf": [ { "additionalProperties": { "type": "string" }, "type": "object" }, { "type": "null" } ], "title": "Definition Vars" }, "init_vars": { "anyOf": [ { "additionalProperties": { "type": "string" }, "type": "object" }, { "type": "null" } ], "title": "Init Vars" } }, "type": "object", "title": "ActionConfigUpdate", "description": "Partial update payload for a parsed action file." }, "ActionCreateRequest": { "properties": { "name": { "type": "string", "title": "Name", "description": "Action base name (e.g. ``my-custom-action``). Must not already exist." }, "actionstart": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actionstart", "description": "Command to execute at jail start." }, "actionstop": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actionstop", "description": "Command to execute at jail stop." }, "actioncheck": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actioncheck", "description": "Command to execute before each ban." }, "actionban": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actionban", "description": "Command to execute to ban an IP." }, "actionunban": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actionunban", "description": "Command to execute to unban an IP." }, "actionflush": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actionflush", "description": "Command to flush all bans on shutdown." }, "definition_vars": { "additionalProperties": { "type": "string" }, "type": "object", "title": "Definition Vars", "description": "Additional ``[Definition]`` variables." }, "init_vars": { "additionalProperties": { "type": "string" }, "type": "object", "title": "Init Vars", "description": "``[Init]`` runtime parameters." } }, "type": "object", "required": [ "name" ], "title": "ActionCreateRequest", "description": "Payload for ``POST /api/config/actions``.\n\nCreates a new user-defined action at ``action.d/{name}.local``." }, "ActionListResponse": { "properties": { "actions": { "items": { "$ref": "#/components/schemas/ActionConfig" }, "type": "array", "title": "Actions", "description": "All discovered actions, each annotated with active/inactive status and the jails that reference them." }, "total": { "type": "integer", "minimum": 0.0, "title": "Total", "description": "Total number of actions found." } }, "type": "object", "required": [ "total" ], "title": "ActionListResponse", "description": "Response for ``GET /api/config/actions``." }, "ActionUpdateRequest": { "properties": { "actionstart": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actionstart", "description": "Updated ``actionstart`` command. ``None`` = keep existing." }, "actionstop": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actionstop", "description": "Updated ``actionstop`` command. ``None`` = keep existing." }, "actioncheck": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actioncheck", "description": "Updated ``actioncheck`` command. ``None`` = keep existing." }, "actionban": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actionban", "description": "Updated ``actionban`` command. ``None`` = keep existing." }, "actionunban": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actionunban", "description": "Updated ``actionunban`` command. ``None`` = keep existing." }, "actionflush": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Actionflush", "description": "Updated ``actionflush`` command. ``None`` = keep existing." }, "definition_vars": { "anyOf": [ { "additionalProperties": { "type": "string" }, "type": "object" }, { "type": "null" } ], "title": "Definition Vars", "description": "Additional ``[Definition]`` variables to set. ``None`` = keep existing." }, "init_vars": { "anyOf": [ { "additionalProperties": { "type": "string" }, "type": "object" }, { "type": "null" } ], "title": "Init Vars", "description": "``[Init]`` parameters to set. ``None`` = keep existing." } }, "type": "object", "title": "ActionUpdateRequest", "description": "Payload for ``PUT /api/config/actions/{name}``.\n\nAccepts only the user-editable ``[Definition]`` lifecycle fields and\n``[Init]`` parameters. Fields left as ``None`` are not changed." }, "ActivateJailRequest": { "properties": { "bantime": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Bantime", "description": "Override ban duration, e.g. ``1h`` or ``3600``." }, "findtime": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Findtime", "description": "Override failure-counting window, e.g. ``10m``." }, "maxretry": { "anyOf": [ { "type": "integer", "minimum": 1.0 }, { "type": "null" } ], "title": "Maxretry", "description": "Override maximum failures before a ban." }, "port": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Port", "description": "Override port(s) to monitor." }, "logpath": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "title": "Logpath", "description": "Override log file paths." } }, "type": "object", "title": "ActivateJailRequest", "description": "Optional override values when activating an inactive jail.\n\nAll fields are optional. Omitted fields are not written to the\n``.local`` override file so that fail2ban falls back to its default\nvalues." }, "ActiveBan": { "properties": { "ip": { "type": "string", "title": "Ip", "description": "Banned IP address." }, "jail": { "type": "string", "title": "Jail", "description": "Jail holding the ban." }, "banned_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Banned At", "description": "ISO 8601 UTC start of the ban." }, "expires_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Expires At", "description": "ISO 8601 UTC expiry, or ``null`` if permanent." }, "ban_count": { "type": "integer", "minimum": 1.0, "title": "Ban Count", "description": "Running ban count for this IP.", "default": 1 }, "country": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Country", "description": "ISO 3166-1 alpha-2 country code." } }, "type": "object", "required": [ "ip", "jail" ], "title": "ActiveBan", "description": "A currently active ban entry returned by ``GET /api/bans/active``." }, "ActiveBanListResponse": { "properties": { "items": { "items": { "$ref": "#/components/schemas/ActiveBan" }, "type": "array", "title": "Items", "description": "Collection items." }, "total": { "type": "integer", "minimum": 0.0, "title": "Total", "description": "Total number of items." } }, "type": "object", "required": [ "total" ], "title": "ActiveBanListResponse", "description": "List of all currently active bans across all jails.\n\nRequest: `GET /api/bans/active` with optional filter parameters.\nResponse: Non-paginated collection of currently active bans with total count.\n\nNote: This endpoint does not support pagination. All matching bans are returned.\nFor paginated results, use individual jail endpoints or the dashboard ban-list view." }, "AddLogPathRequest": { "properties": { "log_path": { "type": "string", "title": "Log Path", "description": "Absolute path to the log file to monitor." }, "tail": { "type": "boolean", "title": "Tail", "description": "If true, monitor from current end of file (tail). If false, read from the beginning.", "default": true } }, "type": "object", "required": [ "log_path" ], "title": "AddLogPathRequest", "description": "Payload for ``POST /api/config/jails/{name}/logpath``." }, "AssignActionRequest": { "properties": { "action_name": { "type": "string", "title": "Action Name", "description": "Action base name to add to the jail (e.g. ``iptables-multiport``)." }, "params": { "additionalProperties": { "type": "string" }, "type": "object", "title": "Params", "description": "Optional per-jail action parameters written as ``action_name[key=value, ...]`` in the jail config." } }, "type": "object", "required": [ "action_name" ], "title": "AssignActionRequest", "description": "Payload for ``POST /api/config/jails/{jail_name}/action``." }, "AssignFilterRequest": { "properties": { "filter_name": { "type": "string", "title": "Filter Name", "description": "Filter base name to assign to the jail (e.g. ``sshd``)." } }, "type": "object", "required": [ "filter_name" ], "title": "AssignFilterRequest", "description": "Payload for ``POST /api/config/jails/{jail_name}/filter``." }, "BanRequest": { "properties": { "ip": { "type": "string", "title": "Ip", "description": "IP address to ban." }, "jail": { "type": "string", "title": "Jail", "description": "Jail in which to apply the ban." } }, "type": "object", "required": [ "ip", "jail" ], "title": "BanRequest", "description": "Payload for ``POST /api/bans`` (ban an IP)." }, "BanTrendBucket": { "properties": { "timestamp": { "type": "string", "title": "Timestamp", "description": "ISO 8601 UTC start of the bucket." }, "count": { "type": "integer", "minimum": 0.0, "title": "Count", "description": "Number of bans that started in this bucket." } }, "type": "object", "required": [ "timestamp", "count" ], "title": "BanTrendBucket", "description": "A single time bucket in the ban trend series." }, "BanTrendResponse": { "properties": { "buckets": { "items": { "$ref": "#/components/schemas/BanTrendBucket" }, "type": "array", "title": "Buckets", "description": "Time-ordered list of ban-count buckets covering the full window." }, "bucket_size": { "type": "string", "title": "Bucket Size", "description": "Human-readable bucket size label (e.g. '1h', '6h', '1d', '7d')." } }, "type": "object", "required": [ "bucket_size" ], "title": "BanTrendResponse", "description": "Response for the ``GET /api/dashboard/bans/trend`` endpoint." }, "BansByCountryResponse": { "properties": { "countries": { "additionalProperties": { "type": "integer" }, "type": "object", "title": "Countries", "description": "ISO 3166-1 alpha-2 country code \u2192 ban count." }, "country_names": { "additionalProperties": { "type": "string" }, "type": "object", "title": "Country Names", "description": "ISO 3166-1 alpha-2 country code \u2192 human-readable country name." }, "bans": { "items": { "$ref": "#/components/schemas/DashboardBanItem" }, "type": "array", "title": "Bans", "description": "All bans in the selected time window (up to the server limit)." }, "total": { "type": "integer", "minimum": 0.0, "title": "Total", "description": "Total ban count in the window." } }, "type": "object", "required": [ "total" ], "title": "BansByCountryResponse", "description": "Response for the bans-by-country aggregation endpoint.\n\nContains a per-country ban count, a human-readable country name map, and\nthe full (un-paginated) ban list for the selected time window so the\nfrontend can render both the world map and its companion table from a\nsingle request." }, "BansByJailResponse": { "properties": { "jails": { "items": { "$ref": "#/components/schemas/JailBanCount" }, "type": "array", "title": "Jails", "description": "Jails ordered by ban count descending." }, "total": { "type": "integer", "minimum": 0.0, "title": "Total", "description": "Total ban count in the selected window." } }, "type": "object", "required": [ "total" ], "title": "BansByJailResponse", "description": "Response for the ``GET /api/dashboard/bans/by-jail`` endpoint." }, "BantimeEscalation": { "properties": { "increment": { "type": "boolean", "title": "Increment", "description": "Whether incremental banning is enabled.", "default": false }, "factor": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "Factor", "description": "Multiplier applied to the base ban time on each repeat offence." }, "formula": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Formula", "description": "Python expression evaluated to compute the escalated ban time." }, "multipliers": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Multipliers", "description": "Space-separated integers used as per-offence multipliers." }, "max_time": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Max Time", "description": "Maximum ban duration in seconds when escalation is active." }, "rnd_time": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Rnd Time", "description": "Random jitter (seconds) added to each escalated ban time." }, "overall_jails": { "type": "boolean", "title": "Overall Jails", "description": "Count repeat offences across all jails, not just the current one.", "default": false } }, "type": "object", "title": "BantimeEscalation", "description": "Incremental ban-time escalation configuration for a jail." }, "BantimeEscalationUpdate": { "properties": { "increment": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "title": "Increment" }, "factor": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "Factor" }, "formula": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Formula" }, "multipliers": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Multipliers" }, "max_time": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Max Time" }, "rnd_time": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Rnd Time" }, "overall_jails": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "title": "Overall Jails" } }, "type": "object", "title": "BantimeEscalationUpdate", "description": "Partial update payload for ban-time escalation settings." }, "BlocklistListResponse": { "properties": { "sources": { "items": { "$ref": "#/components/schemas/BlocklistSource" }, "type": "array", "title": "Sources" } }, "type": "object", "title": "BlocklistListResponse", "description": "Response for ``GET /api/blocklists``." }, "BlocklistSource": { "properties": { "id": { "type": "integer", "title": "Id" }, "name": { "type": "string", "title": "Name" }, "url": { "type": "string", "title": "Url" }, "enabled": { "type": "boolean", "title": "Enabled" }, "created_at": { "type": "string", "title": "Created At" }, "updated_at": { "type": "string", "title": "Updated At" } }, "type": "object", "required": [ "id", "name", "url", "enabled", "created_at", "updated_at" ], "title": "BlocklistSource", "description": "Domain model for a blocklist source definition." }, "BlocklistSourceCreate": { "properties": { "name": { "type": "string", "maxLength": 100, "minLength": 1, "title": "Name", "description": "Human-readable source name." }, "url": { "type": "string", "minLength": 1, "format": "uri", "title": "Url", "description": "URL of the blocklist file (http/https only)." }, "enabled": { "type": "boolean", "title": "Enabled", "default": true } }, "type": "object", "required": [ "name", "url" ], "title": "BlocklistSourceCreate", "description": "Payload for ``POST /api/blocklists``.\n\nURL must use http/https scheme. The hostname must resolve to a public IP\n(not private, loopback, link-local, or reserved). Validation happens\nasynchronously in the service layer." }, "BlocklistSourceUpdate": { "properties": { "name": { "anyOf": [ { "type": "string", "maxLength": 100, "minLength": 1 }, { "type": "null" } ], "title": "Name" }, "url": { "anyOf": [ { "type": "string", "minLength": 1, "format": "uri" }, { "type": "null" } ], "title": "Url" }, "enabled": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "title": "Enabled" } }, "type": "object", "title": "BlocklistSourceUpdate", "description": "Payload for ``PUT /api/blocklists/{id}``. All fields are optional.\n\nIf URL is provided, it must use http/https scheme." }, "ComponentHealth": { "properties": { "name": { "type": "string", "title": "Name", "description": "Component name." }, "healthy": { "type": "boolean", "title": "Healthy", "description": "True when the component is operational." }, "message": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Message", "description": "Optional detail message, e.g. error description." } }, "type": "object", "required": [ "name", "healthy" ], "title": "ComponentHealth", "description": "Health status of a single application component.\n\nFields:\n name: Human-readable component name.\n healthy: True when the component is operational.\n message: Optional detail message (e.g., error description)." }, "ConfFileContent": { "properties": { "name": { "type": "string", "title": "Name", "description": "Base name without extension." }, "filename": { "type": "string", "title": "Filename", "description": "Actual filename." }, "content": { "type": "string", "title": "Content", "description": "Raw file content." } }, "type": "object", "required": [ "name", "filename", "content" ], "title": "ConfFileContent", "description": "A conf file with its raw text content." }, "ConfFileCreateRequest": { "properties": { "name": { "type": "string", "title": "Name", "description": "New file base name (without extension). Must contain only alphanumeric characters, hyphens, underscores, and dots." }, "content": { "type": "string", "title": "Content", "description": "Initial raw file content (must not exceed 512 KB)." } }, "type": "object", "required": [ "name", "content" ], "title": "ConfFileCreateRequest", "description": "Payload for ``POST /api/config/filters`` and ``POST /api/config/actions``." }, "ConfFileEntry": { "properties": { "name": { "type": "string", "title": "Name", "description": "Base name without extension (e.g. ``sshd``)." }, "filename": { "type": "string", "title": "Filename", "description": "Actual filename (e.g. ``sshd.conf``)." } }, "type": "object", "required": [ "name", "filename" ], "title": "ConfFileEntry", "description": "Metadata for a single ``.conf`` or ``.local`` file." }, "ConfFileUpdateRequest": { "properties": { "content": { "type": "string", "title": "Content", "description": "New raw file content (must not exceed 512 KB)." } }, "type": "object", "required": [ "content" ], "title": "ConfFileUpdateRequest", "description": "Payload for ``PUT /api/config/filters/{name}`` and ``PUT /api/config/actions/{name}``." }, "ConfFilesResponse": { "properties": { "files": { "items": { "$ref": "#/components/schemas/ConfFileEntry" }, "type": "array", "title": "Files" }, "total": { "type": "integer", "minimum": 0.0, "title": "Total" } }, "type": "object", "required": [ "total" ], "title": "ConfFilesResponse", "description": "Response for list endpoints (``GET /api/config/filters`` and ``GET /api/config/actions``)." }, "DashboardBanItem": { "properties": { "ip": { "type": "string", "title": "Ip", "description": "Banned IP address." }, "jail": { "type": "string", "title": "Jail", "description": "Jail that issued the ban." }, "banned_at": { "type": "string", "title": "Banned At", "description": "ISO 8601 UTC timestamp of the ban." }, "service": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Service", "description": "First matched log line \u2014 used as context for the ban." }, "country_code": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Country Code", "description": "ISO 3166-1 alpha-2 country code, or ``null`` if unknown." }, "country_name": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Country Name", "description": "Human-readable country name, or ``null`` if unknown." }, "asn": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Asn", "description": "Autonomous System Number string (e.g. ``'AS3320'``)." }, "org": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Org", "description": "Organisation name associated with the IP." }, "ban_count": { "type": "integer", "minimum": 1.0, "title": "Ban Count", "description": "How many times this IP was banned." }, "origin": { "type": "string", "enum": [ "blocklist", "selfblock" ], "title": "Origin", "description": "Whether this ban came from a blocklist import or fail2ban itself." } }, "type": "object", "required": [ "ip", "jail", "banned_at", "ban_count", "origin" ], "title": "DashboardBanItem", "description": "A single row in the dashboard ban-list table.\n\nPopulated from the fail2ban database and enriched with geo data." }, "DashboardBanListResponse": { "properties": { "items": { "items": { "$ref": "#/components/schemas/DashboardBanItem" }, "type": "array", "title": "Items", "description": "Data items for the current page." }, "pagination": { "$ref": "#/components/schemas/PaginationMetadata", "description": "Pagination metadata with computed derived fields." } }, "type": "object", "required": [ "pagination" ], "title": "DashboardBanListResponse", "description": "Paginated dashboard ban-list response.\n\nRequest: `GET /api/dashboard/bans` with time range, page, and filter parameters.\nResponse: Paginated collection of dashboard ban items with geo-enrichment." }, "Fail2BanLogResponse": { "properties": { "log_path": { "type": "string", "title": "Log Path", "description": "Resolved absolute path of the log file being read." }, "lines": { "items": { "type": "string" }, "type": "array", "title": "Lines", "description": "Log lines returned (tail, optionally filtered)." }, "total_lines": { "type": "integer", "minimum": 0.0, "title": "Total Lines", "description": "Total number of lines in the file before filtering." }, "log_level": { "type": "string", "title": "Log Level", "description": "Current fail2ban log level." }, "log_target": { "type": "string", "title": "Log Target", "description": "Current fail2ban log target (file path or special value)." } }, "type": "object", "required": [ "log_path", "total_lines", "log_level", "log_target" ], "title": "Fail2BanLogResponse", "description": "Response for ``GET /api/config/fail2ban-log``." }, "FilterConfig": { "properties": { "name": { "type": "string", "title": "Name", "description": "Filter base name, e.g. ``sshd``." }, "filename": { "type": "string", "title": "Filename", "description": "Actual filename, e.g. ``sshd.conf``." }, "before": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Before", "description": "Included file read before this one." }, "after": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "After", "description": "Included file read after this one." }, "variables": { "additionalProperties": { "type": "string" }, "type": "object", "title": "Variables", "description": "Free-form ``[DEFAULT]`` section variables." }, "prefregex": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Prefregex", "description": "Prefix regex prepended to every failregex." }, "failregex": { "items": { "type": "string" }, "type": "array", "title": "Failregex", "description": "Failure detection regex patterns (one per list entry)." }, "ignoreregex": { "items": { "type": "string" }, "type": "array", "title": "Ignoreregex", "description": "Regex patterns that bypass ban logic." }, "maxlines": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Maxlines", "description": "Maximum number of log lines accumulated for a single match attempt." }, "datepattern": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Datepattern", "description": "Custom date-parsing pattern, or ``None`` for auto-detect." }, "journalmatch": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Journalmatch", "description": "Systemd journal match expression." }, "active": { "type": "boolean", "title": "Active", "description": "``True`` when this filter is referenced by at least one currently enabled (running) jail.", "default": false }, "used_by_jails": { "items": { "type": "string" }, "type": "array", "title": "Used By Jails", "description": "Names of currently enabled jails that reference this filter. Empty when ``active`` is ``False``." }, "source_file": { "type": "string", "title": "Source File", "description": "Absolute path to the ``.conf`` source file for this filter.", "default": "" }, "has_local_override": { "type": "boolean", "title": "Has Local Override", "description": "``True`` when a ``.local`` override file exists alongside the base ``.conf`` file.", "default": false } }, "type": "object", "required": [ "name", "filename" ], "title": "FilterConfig", "description": "Structured representation of a ``filter.d/*.conf`` file.\n\nThe ``active``, ``used_by_jails``, ``source_file``, and\n``has_local_override`` fields are populated by\n:func:`~app.services.filter_config_service.list_filters` and\n:func:`~app.services.filter_config_service.get_filter`. When the model is\nreturned from the raw file-based endpoints (``/filters/{name}/parsed``),\nthese fields carry their default values." }, "FilterConfigUpdate": { "properties": { "before": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Before" }, "after": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "After" }, "variables": { "anyOf": [ { "additionalProperties": { "type": "string" }, "type": "object" }, { "type": "null" } ], "title": "Variables" }, "prefregex": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Prefregex" }, "failregex": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "title": "Failregex" }, "ignoreregex": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "title": "Ignoreregex" }, "maxlines": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Maxlines" }, "datepattern": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Datepattern" }, "journalmatch": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Journalmatch" } }, "type": "object", "title": "FilterConfigUpdate", "description": "Partial update payload for a parsed filter file.\n\nOnly explicitly set (non-``None``) fields are written back." }, "FilterCreateRequest": { "properties": { "name": { "type": "string", "title": "Name", "description": "Filter base name (e.g. ``my-custom-filter``). Must not already exist in ``filter.d/``." }, "failregex": { "items": { "type": "string" }, "type": "array", "title": "Failregex", "description": "Failure-detection regex patterns." }, "ignoreregex": { "items": { "type": "string" }, "type": "array", "title": "Ignoreregex", "description": "Regex patterns that bypass ban logic." }, "prefregex": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Prefregex", "description": "Prefix regex prepended to every failregex." }, "datepattern": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Datepattern", "description": "Custom date-parsing pattern." }, "journalmatch": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Journalmatch", "description": "Systemd journal match expression." } }, "type": "object", "required": [ "name" ], "title": "FilterCreateRequest", "description": "Payload for ``POST /api/config/filters``.\n\nCreates a new user-defined filter at ``filter.d/{name}.local``." }, "FilterListResponse": { "properties": { "filters": { "items": { "$ref": "#/components/schemas/FilterConfig" }, "type": "array", "title": "Filters", "description": "All discovered filters, each annotated with active/inactive status and the jails that reference them." }, "total": { "type": "integer", "minimum": 0.0, "title": "Total", "description": "Total number of filters found." } }, "type": "object", "required": [ "total" ], "title": "FilterListResponse", "description": "Response for ``GET /api/config/filters``." }, "FilterUpdateRequest": { "properties": { "failregex": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "title": "Failregex", "description": "Updated failure-detection regex patterns. ``None`` = keep existing." }, "ignoreregex": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "title": "Ignoreregex", "description": "Updated bypass-ban regex patterns. ``None`` = keep existing." }, "datepattern": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Datepattern", "description": "Custom date-parsing pattern. ``None`` = keep existing." }, "journalmatch": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Journalmatch", "description": "Systemd journal match expression. ``None`` = keep existing." } }, "type": "object", "title": "FilterUpdateRequest", "description": "Payload for ``PUT /api/config/filters/{name}``.\n\nAccepts only the user-editable ``[Definition]`` fields. Fields left as\n``None`` are not changed; the existing value from the merged conf/local is\npreserved." }, "FlushLogsResponse": { "properties": { "message": { "type": "string", "title": "Message", "description": "Human-readable result message from fail2ban." } }, "type": "object", "required": [ "message" ], "title": "FlushLogsResponse", "description": "Standardized response for the flush-logs command endpoint.\n\nFields:\n message: Human-readable result message from fail2ban.\n\nExample:\n ```python\n {\"message\": \"Success: fail2ban log files were flushed.\"}\n ```" }, "GeoCacheStatsResponse": { "properties": { "cache_size": { "type": "integer", "title": "Cache Size", "description": "Number of positive entries in the in-memory cache." }, "unresolved": { "type": "integer", "title": "Unresolved", "description": "Number of geo_cache rows with country_code IS NULL." }, "neg_cache_size": { "type": "integer", "title": "Neg Cache Size", "description": "Number of entries in the in-memory negative cache." }, "dirty_size": { "type": "integer", "title": "Dirty Size", "description": "Number of newly resolved entries not yet flushed to disk." }, "hits": { "type": "integer", "title": "Hits", "description": "Number of cache hits since last clear.", "default": 0 }, "misses": { "type": "integer", "title": "Misses", "description": "Number of cache misses since last clear.", "default": 0 } }, "type": "object", "required": [ "cache_size", "unresolved", "neg_cache_size", "dirty_size" ], "title": "GeoCacheStatsResponse", "description": "Response for ``GET /api/geo/stats``.\n\nExposes diagnostic counters of the geo cache subsystem so operators\ncan assess resolution health from the UI or CLI." }, "GeoDetail": { "properties": { "country_code": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Country Code", "description": "ISO 3166-1 alpha-2 country code." }, "country_name": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Country Name", "description": "Human-readable country name." }, "asn": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Asn", "description": "Autonomous System Number (e.g. ``'AS3320'``)." }, "org": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Org", "description": "Organisation associated with the ASN." } }, "type": "object", "title": "GeoDetail", "description": "Enriched geolocation data for an IP address.\n\nPopulated from the ip-api.com free API." }, "GeoReResolveResponse": { "properties": { "resolved": { "type": "integer", "title": "Resolved", "description": "Number of IPs successfully resolved." }, "total": { "type": "integer", "title": "Total", "description": "Number of IPs retried." } }, "type": "object", "required": [ "resolved", "total" ], "title": "GeoReResolveResponse", "description": "Response for ``POST /api/geo/re-resolve``.\n\nReports how many previously unresolved IPs were retried and how many\ngained a resolved country code after the re-resolve operation." }, "GlobalConfigResponse": { "properties": { "log_level": { "type": "string", "enum": [ "CRITICAL", "ERROR", "WARNING", "NOTICE", "INFO", "DEBUG" ], "title": "Log Level" }, "log_target": { "type": "string", "title": "Log Target", "description": "Log target: STDOUT, STDERR, SYSLOG, or a validated file path." }, "db_purge_age": { "type": "integer", "title": "Db Purge Age", "description": "Seconds after which ban records are purged from the fail2ban DB." }, "db_max_matches": { "type": "integer", "title": "Db Max Matches", "description": "Maximum stored log-line matches per ban record." } }, "type": "object", "required": [ "log_level", "log_target", "db_purge_age", "db_max_matches" ], "title": "GlobalConfigResponse", "description": "Response for ``GET /api/config/global``." }, "GlobalConfigUpdate": { "properties": { "log_level": { "anyOf": [ { "type": "string", "enum": [ "CRITICAL", "ERROR", "WARNING", "NOTICE", "INFO", "DEBUG" ] }, { "type": "null" } ], "title": "Log Level", "description": "Log level: CRITICAL, ERROR, WARNING, NOTICE, INFO, or DEBUG." }, "log_target": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Log Target", "description": "Log target: STDOUT, STDERR, SYSLOG, or a validated file path." }, "db_purge_age": { "anyOf": [ { "type": "integer", "minimum": 0.0 }, { "type": "null" } ], "title": "Db Purge Age" }, "db_max_matches": { "anyOf": [ { "type": "integer", "minimum": 0.0 }, { "type": "null" } ], "title": "Db Max Matches" } }, "type": "object", "title": "GlobalConfigUpdate", "description": "Payload for ``PUT /api/config/global``." }, "HTTPValidationError": { "properties": { "detail": { "items": { "$ref": "#/components/schemas/ValidationError" }, "type": "array", "title": "Detail" } }, "type": "object", "title": "HTTPValidationError" }, "HealthResponse": { "properties": { "status": { "type": "string", "enum": [ "ok", "degraded", "unavailable" ], "title": "Status", "description": "Application health status: 'ok' when healthy, 'degraded' when some components are unhealthy, 'unavailable' when fail2ban is offline." }, "fail2ban": { "type": "string", "enum": [ "online", "offline" ], "title": "Fail2Ban", "description": "fail2ban daemon status: 'online' when reachable, 'offline' otherwise." }, "database": { "type": "string", "enum": [ "ok", "error" ], "title": "Database", "description": "Database connectivity: 'ok' when accessible, 'error' when not." }, "scheduler": { "type": "string", "enum": [ "running", "stopped", "unknown" ], "title": "Scheduler", "description": "Background scheduler status: 'running', 'stopped', or 'unknown'." }, "cache": { "type": "string", "enum": [ "initialised", "uninitialised" ], "title": "Cache", "description": "Cache initialization status: 'initialised' when ready, 'uninitialised' when not." }, "external_logging": { "type": "string", "enum": [ "ok", "error", "disabled" ], "title": "External Logging", "description": "External logging handler status: 'ok' when operational, 'error' when initialization failed, 'disabled' when external logging is not configured." }, "components": { "items": { "$ref": "#/components/schemas/ComponentHealth" }, "type": "array", "title": "Components", "description": "Per-component health detail list. Empty when status is 'ok'." } }, "type": "object", "required": [ "status", "fail2ban", "database", "scheduler", "cache", "external_logging" ], "title": "HealthResponse", "description": "Standardized response for the health check endpoint.\n\nFields:\n status: Application health status \u2014 'ok' when all components are healthy,\n 'degraded' when some components are unhealthy but the service can still\n handle requests, 'unavailable' when fail2ban is offline.\n fail2ban: fail2ban daemon status \u2014 'online' or 'offline'.\n database: Database connectivity \u2014 'ok' or 'error'.\n scheduler: Background scheduler status \u2014 'running', 'stopped', or 'unknown'.\n cache: Cache initialization status \u2014 'initialised' or 'uninitialised'.\n external_logging: External logging handler status \u2014 'ok', 'error', or 'disabled'.\n components: Per-component health detail list (empty when all healthy).\n\nExample:\n ```python\n # Healthy (HTTP 200)\n {\n \"status\": \"ok\",\n \"fail2ban\": \"online\",\n \"database\": \"ok\",\n \"scheduler\": \"running\",\n \"cache\": \"initialised\",\n \"external_logging\": \"disabled\",\n \"components\": []\n }\n\n # Unhealthy (HTTP 503)\n {\n \"status\": \"unavailable\",\n \"fail2ban\": \"offline\",\n \"database\": \"ok\",\n \"scheduler\": \"running\",\n \"cache\": \"initialised\",\n \"external_logging\": \"ok\",\n \"components\": [{\"name\": \"fail2ban\", \"healthy\": false, \"message\": \"Socket not reachable\"}]\n }\n ```" }, "HistoryBanItem": { "properties": { "ip": { "type": "string", "title": "Ip", "description": "Banned IP address." }, "jail": { "type": "string", "title": "Jail", "description": "Jail that issued the ban." }, "banned_at": { "type": "string", "title": "Banned At", "description": "ISO 8601 UTC timestamp of the ban." }, "ban_count": { "type": "integer", "minimum": 1.0, "title": "Ban Count", "description": "How many times this IP was banned." }, "failures": { "type": "integer", "minimum": 0.0, "title": "Failures", "description": "Total failure count extracted from the ``data`` column.", "default": 0 }, "matches": { "items": { "type": "string" }, "type": "array", "title": "Matches", "description": "Matched log lines stored in the ``data`` column." }, "country_code": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Country Code", "description": "ISO 3166-1 alpha-2 country code, or ``null`` if unknown." }, "country_name": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Country Name", "description": "Human-readable country name, or ``null`` if unknown." }, "asn": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Asn", "description": "Autonomous System Number string (e.g. ``'AS3320'``)." }, "org": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Org", "description": "Organisation name associated with the IP." } }, "type": "object", "required": [ "ip", "jail", "banned_at", "ban_count" ], "title": "HistoryBanItem", "description": "A single row in the history ban-list table.\n\nPopulated from the fail2ban database and optionally enriched with\ngeolocation data." }, "HistoryListResponse": { "properties": { "items": { "items": { "$ref": "#/components/schemas/HistoryBanItem" }, "type": "array", "title": "Items", "description": "Data items for the current page." }, "pagination": { "$ref": "#/components/schemas/PaginationMetadata", "description": "Pagination metadata with computed derived fields." } }, "type": "object", "required": [ "pagination" ], "title": "HistoryListResponse", "description": "Paginated history ban-list response.\n\nRequest: ``GET /api/history`` with optional time-range, jail, IP, and\norigin filters plus pagination parameters.\nResponse: Paginated collection of historical ban records with geolocation." }, "IgnoreIpRequest": { "properties": { "ip": { "type": "string", "title": "Ip", "description": "IP address or CIDR network to ignore." } }, "type": "object", "required": [ "ip" ], "title": "IgnoreIpRequest", "description": "Payload for adding an IP or network to a jail's ignore list." }, "IgnoreListResponse": { "properties": { "items": { "items": { "type": "string" }, "type": "array", "title": "Items", "description": "Collection items." }, "total": { "type": "integer", "minimum": 0.0, "title": "Total", "description": "Total number of items." } }, "type": "object", "required": [ "total" ], "title": "IgnoreListResponse", "description": "Response for ``GET /api/jails/{name}/ignoreip``.\n\nReturns the jailed ignore list as a standard collection response." }, "ImportLogEntry": { "properties": { "id": { "type": "integer", "title": "Id" }, "source_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Source Id" }, "source_url": { "type": "string", "title": "Source Url" }, "timestamp": { "type": "integer", "title": "Timestamp" }, "ips_imported": { "type": "integer", "title": "Ips Imported" }, "ips_skipped": { "type": "integer", "title": "Ips Skipped" }, "errors": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Errors" } }, "type": "object", "required": [ "id", "source_id", "source_url", "timestamp", "ips_imported", "ips_skipped", "errors" ], "title": "ImportLogEntry", "description": "A single blocklist import run record." }, "ImportLogListResponse": { "properties": { "items": { "items": { "$ref": "#/components/schemas/ImportLogEntry" }, "type": "array", "title": "Items", "description": "Data items for the current page." }, "pagination": { "$ref": "#/components/schemas/PaginationMetadata", "description": "Pagination metadata with computed derived fields." } }, "type": "object", "required": [ "pagination" ], "title": "ImportLogListResponse", "description": "Response for ``GET /api/blocklists/log``.\n\nPaginated list of all blocklist import runs with timestamps, source info,\nand per-source import/skip counts." }, "ImportRunResult": { "properties": { "results": { "items": { "$ref": "#/components/schemas/ImportSourceResult" }, "type": "array", "title": "Results" }, "total_imported": { "type": "integer", "title": "Total Imported" }, "total_skipped": { "type": "integer", "title": "Total Skipped" }, "errors_count": { "type": "integer", "title": "Errors Count" } }, "type": "object", "required": [ "total_imported", "total_skipped", "errors_count" ], "title": "ImportRunResult", "description": "Aggregated result from a full import run across all enabled sources." }, "ImportSourceResult": { "properties": { "source_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Source Id" }, "source_url": { "type": "string", "title": "Source Url" }, "ips_imported": { "type": "integer", "title": "Ips Imported" }, "ips_skipped": { "type": "integer", "title": "Ips Skipped" }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error" } }, "type": "object", "required": [ "source_id", "source_url", "ips_imported", "ips_skipped", "error" ], "title": "ImportSourceResult", "description": "Result of importing a single blocklist source." }, "InactiveJail": { "properties": { "name": { "type": "string", "title": "Name", "description": "Jail name from the config section header." }, "filter": { "type": "string", "title": "Filter", "description": "Filter name used by this jail. May include fail2ban mode suffix, e.g. ``sshd[mode=normal]``." }, "actions": { "items": { "type": "string" }, "type": "array", "title": "Actions", "description": "Action references listed in the config (raw strings)." }, "port": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Port", "description": "Port(s) to monitor, e.g. ``ssh`` or ``22,2222``." }, "logpath": { "items": { "type": "string" }, "type": "array", "title": "Logpath", "description": "Log file paths to monitor." }, "bantime": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Bantime", "description": "Ban duration as a raw config string, e.g. ``10m`` or ``-1``." }, "findtime": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Findtime", "description": "Failure-counting window as a raw config string, e.g. ``10m``." }, "maxretry": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Maxretry", "description": "Number of failures before a ban is issued." }, "ban_time_seconds": { "type": "integer", "title": "Ban Time Seconds", "description": "Ban duration in seconds, parsed from bantime string.", "default": 600 }, "find_time_seconds": { "type": "integer", "title": "Find Time Seconds", "description": "Failure-counting window in seconds, parsed from findtime string.", "default": 600 }, "log_encoding": { "type": "string", "enum": [ "auto", "ascii", "utf-8", "UTF-8", "latin-1" ], "title": "Log Encoding", "description": "Log encoding, e.g. ``utf-8`` or ``auto``.", "default": "auto" }, "backend": { "type": "string", "enum": [ "auto", "polling", "pyinotify", "systemd", "gamin" ], "title": "Backend", "description": "Log-monitoring backend, e.g. ``auto``, ``pyinotify``, ``polling``.", "default": "auto" }, "date_pattern": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Date Pattern", "description": "Date pattern for log parsing, or None for auto-detect." }, "use_dns": { "type": "string", "enum": [ "yes", "warn", "no", "raw" ], "title": "Use Dns", "description": "DNS resolution mode: ``yes``, ``warn``, ``no``, or ``raw``.", "default": "warn" }, "prefregex": { "type": "string", "title": "Prefregex", "description": "Prefix regex prepended to every failregex.", "default": "" }, "fail_regex": { "items": { "type": "string" }, "type": "array", "title": "Fail Regex", "description": "List of failure regex patterns." }, "ignore_regex": { "items": { "type": "string" }, "type": "array", "title": "Ignore Regex", "description": "List of ignore regex patterns." }, "bantime_escalation": { "anyOf": [ { "$ref": "#/components/schemas/BantimeEscalation" }, { "type": "null" } ], "description": "Ban-time escalation configuration, if enabled." }, "source_file": { "type": "string", "title": "Source File", "description": "Absolute path to the config file where this jail is defined." }, "enabled": { "type": "boolean", "title": "Enabled", "description": "Effective ``enabled`` value from the merged config. ``False`` for inactive jails that appear in this list." }, "has_local_override": { "type": "boolean", "title": "Has Local Override", "description": "``True`` when a ``jail.d/{name}.local`` file exists for this jail. Only meaningful for inactive jails; indicates that a cleanup action is available.", "default": false } }, "type": "object", "required": [ "name", "filter", "source_file", "enabled" ], "title": "InactiveJail", "description": "A jail defined in fail2ban config files that is not currently active.\n\nA jail is considered inactive when its ``enabled`` key is ``false`` (or\nabsent from the config, since fail2ban defaults to disabled) **or** when it\nis explicitly enabled in config but fail2ban is not reporting it as\nrunning." }, "InactiveJailListResponse": { "properties": { "items": { "items": { "$ref": "#/components/schemas/InactiveJail" }, "type": "array", "title": "Items", "description": "Collection items." }, "total": { "type": "integer", "minimum": 0.0, "title": "Total", "description": "Total number of items." } }, "type": "object", "required": [ "total" ], "title": "InactiveJailListResponse", "description": "Response for ``GET /api/config/jails/inactive``.\n\nReturns a non-paginated collection of inactive jail configurations." }, "IpDetailResponse": { "properties": { "ip": { "type": "string", "title": "Ip", "description": "The IP address." }, "total_bans": { "type": "integer", "minimum": 0.0, "title": "Total Bans", "description": "Total number of ban records." }, "total_failures": { "type": "integer", "minimum": 0.0, "title": "Total Failures", "description": "Sum of all failure counts across all ban events." }, "last_ban_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Last Ban At", "description": "ISO 8601 UTC timestamp of the most recent ban, or ``null``." }, "country_code": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Country Code", "description": "ISO 3166-1 alpha-2 country code, or ``null`` if unknown." }, "country_name": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Country Name", "description": "Human-readable country name, or ``null`` if unknown." }, "asn": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Asn", "description": "Autonomous System Number string." }, "org": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Org", "description": "Organisation name associated with the IP." }, "timeline": { "items": { "$ref": "#/components/schemas/IpTimelineEvent" }, "type": "array", "title": "Timeline", "description": "All ban events for this IP, ordered newest-first." } }, "type": "object", "required": [ "ip", "total_bans", "total_failures" ], "title": "IpDetailResponse", "description": "Full historical record for a single IP address.\n\nContains aggregated totals and a chronological timeline of all ban events\nrecorded in the fail2ban database for the given IP." }, "IpLookupResponse": { "properties": { "ip": { "type": "string", "title": "Ip", "description": "The queried IP address." }, "currently_banned_in": { "items": { "type": "string" }, "type": "array", "title": "Currently Banned In", "description": "Names of jails where this IP is currently banned." }, "geo": { "anyOf": [ { "$ref": "#/components/schemas/GeoDetail" }, { "type": "null" } ], "description": "Enriched geographical and network information." } }, "type": "object", "required": [ "ip" ], "title": "IpLookupResponse", "description": "Response for ``GET /api/geo/lookup/{ip}``.\n\nAggregates current ban status and geographical information for an IP." }, "IpTimelineEvent": { "properties": { "jail": { "type": "string", "title": "Jail", "description": "Jail that triggered this ban." }, "banned_at": { "type": "string", "title": "Banned At", "description": "ISO 8601 UTC timestamp of the ban." }, "ban_count": { "type": "integer", "minimum": 1.0, "title": "Ban Count", "description": "Running ban counter for this IP at the time of this event." }, "failures": { "type": "integer", "minimum": 0.0, "title": "Failures", "description": "Failure count at the time of the ban.", "default": 0 }, "matches": { "items": { "type": "string" }, "type": "array", "title": "Matches", "description": "Matched log lines that triggered the ban." } }, "type": "object", "required": [ "jail", "banned_at", "ban_count" ], "title": "IpTimelineEvent", "description": "A single ban event in a per-IP timeline.\n\nRepresents one row from the fail2ban ``bans`` table for a specific IP." }, "Jail": { "properties": { "name": { "type": "string", "title": "Name", "description": "Jail name as configured in fail2ban." }, "enabled": { "type": "boolean", "title": "Enabled", "description": "Whether the jail is currently active." }, "running": { "type": "boolean", "title": "Running", "description": "Whether the jail backend is running." }, "idle": { "type": "boolean", "title": "Idle", "description": "Whether the jail is in idle mode.", "default": false }, "backend": { "type": "string", "title": "Backend", "description": "Log monitoring backend (e.g. polling, systemd)." }, "log_paths": { "items": { "type": "string" }, "type": "array", "title": "Log Paths", "description": "Monitored log files." }, "fail_regex": { "items": { "type": "string" }, "type": "array", "title": "Fail Regex", "description": "Failure detection regex patterns." }, "ignore_regex": { "items": { "type": "string" }, "type": "array", "title": "Ignore Regex", "description": "Regex patterns that bypass the ban logic." }, "ignore_ips": { "items": { "type": "string" }, "type": "array", "title": "Ignore Ips", "description": "IP addresses or CIDRs on the ignore list." }, "date_pattern": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Date Pattern", "description": "Custom date pattern for log parsing." }, "log_encoding": { "type": "string", "title": "Log Encoding", "description": "Log file encoding.", "default": "UTF-8" }, "find_time": { "type": "integer", "title": "Find Time", "description": "Time window (seconds) for counting failures." }, "ban_time": { "type": "integer", "title": "Ban Time", "description": "Duration (seconds) of a ban. -1 means permanent." }, "max_retry": { "type": "integer", "title": "Max Retry", "description": "Number of failures before a ban is issued." }, "actions": { "items": { "type": "string" }, "type": "array", "title": "Actions", "description": "Names of actions attached to this jail." }, "bantime_escalation": { "anyOf": [ { "$ref": "#/components/schemas/BantimeEscalation" }, { "type": "null" } ], "description": "Incremental ban-time escalation settings, or None if not configured." }, "status": { "anyOf": [ { "$ref": "#/components/schemas/JailStatus" }, { "type": "null" } ], "description": "Runtime counters." } }, "type": "object", "required": [ "name", "enabled", "running", "backend", "find_time", "ban_time", "max_retry" ], "title": "Jail", "description": "Domain model for a single fail2ban jail with its full configuration." }, "JailActivationResponse": { "properties": { "name": { "type": "string", "title": "Name", "description": "Name of the affected jail." }, "active": { "type": "boolean", "title": "Active", "description": "New activation state: ``True`` after activate, ``False`` after deactivate." }, "message": { "type": "string", "title": "Message", "description": "Human-readable result message." }, "fail2ban_running": { "type": "boolean", "title": "Fail2Ban Running", "description": "Whether the fail2ban daemon is still running after the activation and reload. ``False`` signals that the daemon may have crashed.", "default": true }, "validation_warnings": { "items": { "type": "string" }, "type": "array", "title": "Validation Warnings", "description": "Non-fatal warnings from the pre-activation validation step." }, "recovered": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "title": "Recovered", "description": "Set when activation failed after writing the config file. ``True`` means the system automatically rolled back the change and restarted fail2ban. ``False`` means the rollback itself also failed and manual intervention is required. ``None`` when activation succeeded or failed before the file was written." } }, "type": "object", "required": [ "name", "active", "message" ], "title": "JailActivationResponse", "description": "Response for jail activation and deactivation endpoints." }, "JailBanCount": { "properties": { "jail": { "type": "string", "title": "Jail", "description": "Jail name." }, "count": { "type": "integer", "minimum": 0.0, "title": "Count", "description": "Number of bans recorded in this jail." } }, "type": "object", "required": [ "jail", "count" ], "title": "JailBanCount", "description": "A single jail entry in the bans-by-jail aggregation." }, "JailBannedIpsResponse": { "properties": { "items": { "items": { "$ref": "#/components/schemas/ActiveBan" }, "type": "array", "title": "Items", "description": "Data items for the current page." }, "pagination": { "$ref": "#/components/schemas/PaginationMetadata", "description": "Pagination metadata with computed derived fields." } }, "type": "object", "required": [ "pagination" ], "title": "JailBannedIpsResponse", "description": "Paginated response for ``GET /api/jails/{name}/banned``.\n\nContains only the current page of active ban entries for a single jail,\ngeo-enriched exclusively for the page slice to avoid rate-limit issues.\n\nRequest: `GET /api/jails/{name}/banned` with page and page_size parameters.\nResponse: Paginated collection of active bans for the specified jail." }, "JailCommandResponse": { "properties": { "message": { "type": "string", "title": "Message", "description": "Human-readable result or error message." }, "success": { "type": "boolean", "title": "Success", "description": "Whether the command succeeded (false for errors in non-exception handlers).", "default": true }, "jail": { "type": "string", "title": "Jail", "description": "Target jail name, or '*' for operations on all jails." } }, "type": "object", "required": [ "message", "jail" ], "title": "JailCommandResponse", "description": "Generic response for jail control commands (start, stop, reload, idle).\n\nExtends the base CommandResponse with a jail field to identify the target." }, "JailConfig": { "properties": { "name": { "type": "string", "title": "Name", "description": "Jail name as configured in fail2ban." }, "ban_time": { "type": "integer", "title": "Ban Time", "description": "Ban duration in seconds. -1 for permanent." }, "max_retry": { "type": "integer", "minimum": 1.0, "title": "Max Retry", "description": "Number of failures before a ban is issued." }, "find_time": { "type": "integer", "minimum": 1.0, "title": "Find Time", "description": "Time window (seconds) for counting failures." }, "fail_regex": { "items": { "type": "string" }, "type": "array", "title": "Fail Regex", "description": "Failure detection regex patterns." }, "ignore_regex": { "items": { "type": "string" }, "type": "array", "title": "Ignore Regex", "description": "Regex patterns that bypass the ban logic." }, "log_paths": { "items": { "type": "string" }, "type": "array", "title": "Log Paths", "description": "Monitored log files." }, "date_pattern": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Date Pattern", "description": "Custom date pattern for log parsing." }, "log_encoding": { "type": "string", "enum": [ "auto", "ascii", "utf-8", "UTF-8", "latin-1" ], "title": "Log Encoding", "description": "Log file encoding.", "default": "UTF-8" }, "backend": { "type": "string", "enum": [ "auto", "polling", "pyinotify", "systemd", "gamin" ], "title": "Backend", "description": "Log monitoring backend.", "default": "polling" }, "use_dns": { "type": "string", "enum": [ "yes", "warn", "no", "raw" ], "title": "Use Dns", "description": "DNS lookup mode: yes | warn | no | raw.", "default": "warn" }, "prefregex": { "type": "string", "title": "Prefregex", "description": "Prefix regex prepended to every failregex; empty means disabled.", "default": "" }, "actions": { "items": { "type": "string" }, "type": "array", "title": "Actions", "description": "Names of actions attached to this jail." }, "bantime_escalation": { "anyOf": [ { "$ref": "#/components/schemas/BantimeEscalation" }, { "type": "null" } ], "description": "Incremental ban-time escalation settings, or None if not configured." } }, "type": "object", "required": [ "name", "ban_time", "max_retry", "find_time" ], "title": "JailConfig", "description": "Configuration snapshot of a single jail (editable fields)." }, "JailConfigFile": { "properties": { "name": { "type": "string", "title": "Name", "description": "Jail name (file stem, e.g. ``sshd``)." }, "filename": { "type": "string", "title": "Filename", "description": "Actual filename (e.g. ``sshd.conf``)." }, "enabled": { "type": "boolean", "title": "Enabled", "description": "Whether the jail is enabled. Derived from the ``enabled`` key inside the file; defaults to ``true`` when the key is absent." } }, "type": "object", "required": [ "name", "filename", "enabled" ], "title": "JailConfigFile", "description": "Metadata for a single jail configuration file in ``jail.d/``." }, "JailConfigFileContent": { "properties": { "name": { "type": "string", "title": "Name", "description": "Jail name (file stem)." }, "filename": { "type": "string", "title": "Filename", "description": "Actual filename." }, "enabled": { "type": "boolean", "title": "Enabled", "description": "Whether the jail is enabled." }, "content": { "type": "string", "title": "Content", "description": "Raw file content." } }, "type": "object", "required": [ "name", "filename", "enabled", "content" ], "title": "JailConfigFileContent", "description": "Single jail config file with its raw content." }, "JailConfigFileEnabledUpdate": { "properties": { "enabled": { "type": "boolean", "title": "Enabled", "description": "New enabled state for this jail." } }, "type": "object", "required": [ "enabled" ], "title": "JailConfigFileEnabledUpdate", "description": "Payload for ``PUT /api/config/jail-files/{filename}/enabled``." }, "JailConfigFilesResponse": { "properties": { "files": { "items": { "$ref": "#/components/schemas/JailConfigFile" }, "type": "array", "title": "Files" }, "total": { "type": "integer", "minimum": 0.0, "title": "Total" } }, "type": "object", "required": [ "total" ], "title": "JailConfigFilesResponse", "description": "Response for ``GET /api/config/jail-files``." }, "JailConfigListResponse": { "properties": { "items": { "items": { "$ref": "#/components/schemas/JailConfig" }, "type": "array", "title": "Items", "description": "Collection items." }, "total": { "type": "integer", "minimum": 0.0, "title": "Total", "description": "Total number of items." } }, "type": "object", "required": [ "total" ], "title": "JailConfigListResponse", "description": "Response for ``GET /api/config/jails``.\n\nReturns a non-paginated collection of jail configurations." }, "JailConfigResponse": { "properties": { "jail": { "$ref": "#/components/schemas/JailConfig" } }, "type": "object", "required": [ "jail" ], "title": "JailConfigResponse", "description": "Response for ``GET /api/config/jails/{name}``." }, "JailConfigUpdate": { "properties": { "ban_time": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Ban Time", "description": "Ban duration in seconds. -1 for permanent." }, "max_retry": { "anyOf": [ { "type": "integer", "minimum": 1.0 }, { "type": "null" } ], "title": "Max Retry" }, "find_time": { "anyOf": [ { "type": "integer", "minimum": 1.0 }, { "type": "null" } ], "title": "Find Time" }, "fail_regex": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "title": "Fail Regex", "description": "Failure detection regex patterns." }, "ignore_regex": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "title": "Ignore Regex" }, "prefregex": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Prefregex", "description": "Prefix regex; None = skip, '' = clear, non-empty = set." }, "date_pattern": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Date Pattern" }, "dns_mode": { "anyOf": [ { "type": "string", "enum": [ "yes", "warn", "no", "raw" ] }, { "type": "null" } ], "title": "Dns Mode", "description": "DNS lookup mode: yes | warn | no | raw." }, "backend": { "anyOf": [ { "type": "string", "enum": [ "auto", "polling", "pyinotify", "systemd", "gamin" ] }, { "type": "null" } ], "title": "Backend", "description": "Log monitoring backend." }, "log_encoding": { "anyOf": [ { "type": "string", "enum": [ "auto", "ascii", "utf-8", "UTF-8", "latin-1" ] }, { "type": "null" } ], "title": "Log Encoding", "description": "Log file encoding." }, "enabled": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "title": "Enabled" }, "bantime_escalation": { "anyOf": [ { "$ref": "#/components/schemas/BantimeEscalationUpdate" }, { "type": "null" } ], "description": "Incremental ban-time escalation settings to update." } }, "type": "object", "title": "JailConfigUpdate", "description": "Payload for ``PUT /api/config/jails/{name}``." }, "JailDetailResponse": { "properties": { "jail": { "$ref": "#/components/schemas/Jail" }, "ignore_list": { "items": { "type": "string" }, "type": "array", "title": "Ignore List", "description": "List of IP addresses and networks currently ignored by the jail." }, "ignore_self": { "type": "boolean", "title": "Ignore Self", "description": "Whether the jail ignores the server's own IP addresses.", "default": false } }, "type": "object", "required": [ "jail" ], "title": "JailDetailResponse", "description": "Response for ``GET /api/jails/{name}``.\n\nIncludes the primary jail object together with supplemental metadata\nrequired by the UI." }, "JailFileConfig": { "properties": { "filename": { "type": "string", "title": "Filename", "description": "Filename including extension (e.g. 'sshd.conf')." }, "jails": { "additionalProperties": { "$ref": "#/components/schemas/JailSectionConfig" }, "type": "object", "title": "Jails", "description": "Mapping of jail name \u2192 settings for each [section] in the file." } }, "type": "object", "required": [ "filename" ], "title": "JailFileConfig", "description": "Structured representation of a jail.d/*.conf file." }, "JailFileConfigUpdate": { "properties": { "jails": { "anyOf": [ { "additionalProperties": { "$ref": "#/components/schemas/JailSectionConfig" }, "type": "object" }, { "type": "null" } ], "title": "Jails", "description": "Jail section updates. Only jails present in this dict are updated." } }, "type": "object", "title": "JailFileConfigUpdate", "description": "Partial update payload for a jail.d file." }, "JailListResponse": { "properties": { "items": { "items": { "$ref": "#/components/schemas/JailSummary" }, "type": "array", "title": "Items", "description": "Collection items." }, "total": { "type": "integer", "minimum": 0.0, "title": "Total", "description": "Total number of items." } }, "type": "object", "required": [ "total" ], "title": "JailListResponse", "description": "Response for ``GET /api/jails``.\n\nReturns a non-paginated collection of jail summaries with their current status." }, "JailSectionConfig": { "properties": { "enabled": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "title": "Enabled", "description": "Whether this jail is enabled." }, "port": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Port", "description": "Port(s) to monitor (e.g. 'ssh' or '22,2222')." }, "filter": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Filter", "description": "Filter name to use (e.g. 'sshd')." }, "logpath": { "items": { "type": "string" }, "type": "array", "title": "Logpath", "description": "Log file paths to monitor." }, "maxretry": { "anyOf": [ { "type": "integer", "minimum": 1.0 }, { "type": "null" } ], "title": "Maxretry", "description": "Failures before banning." }, "findtime": { "anyOf": [ { "type": "integer", "minimum": 1.0 }, { "type": "null" } ], "title": "Findtime", "description": "Time window in seconds for counting failures." }, "bantime": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Bantime", "description": "Ban duration in seconds. -1 for permanent." }, "action": { "items": { "type": "string" }, "type": "array", "title": "Action", "description": "Action references." }, "backend": { "anyOf": [ { "type": "string", "enum": [ "auto", "polling", "pyinotify", "systemd", "gamin" ] }, { "type": "null" } ], "title": "Backend", "description": "Log monitoring backend." }, "extra": { "additionalProperties": { "type": "string" }, "type": "object", "title": "Extra", "description": "Additional settings not captured by named fields." } }, "type": "object", "title": "JailSectionConfig", "description": "Settings within a single [jailname] section of a jail.d file." }, "JailStatus": { "properties": { "currently_banned": { "type": "integer", "minimum": 0.0, "title": "Currently Banned" }, "total_banned": { "type": "integer", "minimum": 0.0, "title": "Total Banned" }, "currently_failed": { "type": "integer", "minimum": 0.0, "title": "Currently Failed" }, "total_failed": { "type": "integer", "minimum": 0.0, "title": "Total Failed" } }, "type": "object", "required": [ "currently_banned", "total_banned", "currently_failed", "total_failed" ], "title": "JailStatus", "description": "Runtime metrics for a single jail." }, "JailSummary": { "properties": { "name": { "type": "string", "title": "Name" }, "enabled": { "type": "boolean", "title": "Enabled" }, "running": { "type": "boolean", "title": "Running" }, "idle": { "type": "boolean", "title": "Idle" }, "backend": { "type": "string", "title": "Backend" }, "find_time": { "type": "integer", "title": "Find Time" }, "ban_time": { "type": "integer", "title": "Ban Time" }, "max_retry": { "type": "integer", "title": "Max Retry" }, "status": { "anyOf": [ { "$ref": "#/components/schemas/JailStatus" }, { "type": "null" } ] } }, "type": "object", "required": [ "name", "enabled", "running", "idle", "backend", "find_time", "ban_time", "max_retry" ], "title": "JailSummary", "description": "Lightweight jail entry for the overview list." }, "JailValidationIssue": { "properties": { "field": { "type": "string", "title": "Field", "description": "Config field associated with this issue, e.g. 'filter', 'failregex', 'logpath'." }, "message": { "type": "string", "title": "Message", "description": "Human-readable description of the issue." } }, "type": "object", "required": [ "field", "message" ], "title": "JailValidationIssue", "description": "A single issue found during pre-activation validation of a jail config." }, "JailValidationResult": { "properties": { "jail_name": { "type": "string", "title": "Jail Name", "description": "Name of the validated jail." }, "valid": { "type": "boolean", "title": "Valid", "description": "True when no issues were found." }, "issues": { "items": { "$ref": "#/components/schemas/JailValidationIssue" }, "type": "array", "title": "Issues", "description": "Validation issues found. Empty when valid=True." } }, "type": "object", "required": [ "jail_name", "valid" ], "title": "JailValidationResult", "description": "Result of pre-activation validation of a single jail configuration." }, "LogPreviewLine": { "properties": { "line": { "type": "string", "title": "Line" }, "matched": { "type": "boolean", "title": "Matched" }, "groups": { "items": { "type": "string" }, "type": "array", "title": "Groups" } }, "type": "object", "required": [ "line", "matched" ], "title": "LogPreviewLine", "description": "A single log line with match information." }, "LogPreviewRequest": { "properties": { "log_path": { "type": "string", "title": "Log Path", "description": "Absolute path to the log file to preview." }, "fail_regex": { "type": "string", "title": "Fail Regex", "description": "Regex pattern to test against log lines." }, "num_lines": { "type": "integer", "maximum": 5000.0, "minimum": 1.0, "title": "Num Lines", "description": "Number of lines to read from the end of the file.", "default": 200 } }, "type": "object", "required": [ "log_path", "fail_regex" ], "title": "LogPreviewRequest", "description": "Payload for ``POST /api/config/preview-log``." }, "LogPreviewResponse": { "properties": { "lines": { "items": { "$ref": "#/components/schemas/LogPreviewLine" }, "type": "array", "title": "Lines" }, "total_lines": { "type": "integer", "minimum": 0.0, "title": "Total Lines" }, "matched_count": { "type": "integer", "minimum": 0.0, "title": "Matched Count" }, "regex_error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Regex Error", "description": "Set if the regex failed to compile." } }, "type": "object", "required": [ "total_lines", "matched_count" ], "title": "LogPreviewResponse", "description": "Response for ``POST /api/config/preview-log``." }, "LoginRequest": { "properties": { "password": { "type": "string", "maxLength": 72, "title": "Password", "description": "Master password to authenticate with (max 72 bytes due to bcrypt truncation)." } }, "type": "object", "required": [ "password" ], "title": "LoginRequest", "description": "Payload for ``POST /api/auth/login``." }, "LoginResponse": { "properties": { "expires_at": { "type": "string", "title": "Expires At", "description": "ISO 8601 UTC expiry timestamp." } }, "type": "object", "required": [ "expires_at" ], "title": "LoginResponse", "description": "Successful login response.\n\nThe session token is set as an ``HttpOnly`` ``SameSite=Lax`` cookie by the\nrouter, protecting it from JavaScript access. The JSON body contains only\nthe expiry timestamp, allowing the frontend to know when to prompt for\nre-authentication.\n\nFor programmatic API clients that require a token in the response body,\nuse ``POST /api/auth/token`` instead, which does not set a cookie." }, "LogoutResponse": { "properties": { "message": { "type": "string", "title": "Message", "default": "Logged out successfully." } }, "type": "object", "title": "LogoutResponse", "description": "Response body for ``POST /api/auth/logout``." }, "MapColorThresholdsResponse": { "properties": { "threshold_high": { "type": "integer", "title": "Threshold High", "description": "Ban count for red coloring." }, "threshold_medium": { "type": "integer", "title": "Threshold Medium", "description": "Ban count for yellow coloring." }, "threshold_low": { "type": "integer", "title": "Threshold Low", "description": "Ban count for green coloring." } }, "type": "object", "required": [ "threshold_high", "threshold_medium", "threshold_low" ], "title": "MapColorThresholdsResponse", "description": "Response for ``GET /api/config/map-thresholds``." }, "MapColorThresholdsUpdate": { "properties": { "threshold_high": { "type": "integer", "exclusiveMinimum": 0.0, "title": "Threshold High", "description": "Ban count for red." }, "threshold_medium": { "type": "integer", "exclusiveMinimum": 0.0, "title": "Threshold Medium", "description": "Ban count for yellow." }, "threshold_low": { "type": "integer", "exclusiveMinimum": 0.0, "title": "Threshold Low", "description": "Ban count for green." } }, "type": "object", "required": [ "threshold_high", "threshold_medium", "threshold_low" ], "title": "MapColorThresholdsUpdate", "description": "Payload for ``PUT /api/config/map-thresholds``." }, "PaginationMetadata": { "properties": { "page": { "type": "integer", "minimum": 1.0, "title": "Page", "description": "Current page number (1-based). Set to 1 for cursor pagination." }, "page_size": { "type": "integer", "minimum": 1.0, "title": "Page Size", "description": "Number of items per page." }, "total": { "type": "integer", "title": "Total", "description": "Total number of items matching the query. -1 if unknown (cursor pagination)." }, "total_pages": { "type": "integer", "title": "Total Pages", "description": "Computed total number of pages. -1 if unknown (cursor pagination)." }, "has_next_page": { "type": "boolean", "title": "Has Next Page", "description": "Whether there is a next page after this one." }, "has_prev_page": { "type": "boolean", "title": "Has Prev Page", "description": "Whether there is a previous page before this one." }, "cursor": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Cursor", "description": "Opaque cursor token for fetching the next page (cursor pagination only)." }, "pagination_mode": { "type": "string", "enum": [ "offset", "cursor" ], "title": "Pagination Mode", "description": "Pagination mode used by the endpoint. 'offset' uses page/page_size; 'cursor' uses cursor tokens.", "default": "offset" } }, "type": "object", "required": [ "page", "page_size", "total", "total_pages", "has_next_page", "has_prev_page" ], "title": "PaginationMetadata", "description": "Pagination metadata embedded in paginated list responses.\n\nContains page information and computed fields to support frontend pagination controls.\nSupports both offset-based and cursor-based pagination modes.\n\nFields:\n page: Current page number (1-based). Set to 1 for cursor pagination.\n page_size: Number of items per page.\n total: Total number of items matching the query (across all pages).\n For cursor pagination, this is -1 (unknown without full scan).\n total_pages: Computed total number of pages.\n For cursor pagination, this is -1 (unknown without full scan).\n has_next_page: Whether there is a next page after this one.\n has_prev_page: Whether there is a previous page before this one.\n Always False for cursor pagination (cannot navigate backward without storing history).\n cursor: Opaque cursor token for fetching the next page (cursor pagination only).\n None for offset pagination or when there are no more pages.\n pagination_mode: Pagination mode used by the endpoint. 'offset' uses page/page_size;\n 'cursor' uses cursor tokens for navigation.\n\nExample (offset pagination):\n ```python\n pagination = PaginationMetadata(\n page=2,\n page_size=50,\n total=150,\n total_pages=3,\n has_next_page=True,\n has_prev_page=True,\n cursor=None,\n pagination_mode=\"offset\",\n )\n ```\n\nExample (cursor pagination):\n ```python\n pagination = PaginationMetadata(\n page=1,\n page_size=50,\n total=-1,\n total_pages=-1,\n has_next_page=True,\n has_prev_page=False,\n cursor=\"eyJpZCI6IDQyN30=\",\n pagination_mode=\"cursor\",\n )\n ```" }, "PendingRecovery": { "properties": { "jail_name": { "type": "string", "title": "Jail Name", "description": "Name of the jail whose activation likely caused the crash." }, "activated_at": { "type": "string", "format": "date-time", "title": "Activated At", "description": "ISO-8601 UTC timestamp of when the jail was activated." }, "detected_at": { "type": "string", "format": "date-time", "title": "Detected At", "description": "ISO-8601 UTC timestamp of when the crash was detected." }, "recovered": { "type": "boolean", "title": "Recovered", "description": "Whether fail2ban has been successfully restarted.", "default": false } }, "type": "object", "required": [ "jail_name", "activated_at", "detected_at" ], "title": "PendingRecovery", "description": "Records a probable activation-caused fail2ban crash pending user action." }, "PreviewResponse": { "properties": { "entries": { "items": { "type": "string" }, "type": "array", "title": "Entries", "description": "Sample of valid IP entries" }, "total_lines": { "type": "integer", "title": "Total Lines" }, "valid_count": { "type": "integer", "title": "Valid Count" }, "skipped_count": { "type": "integer", "title": "Skipped Count" } }, "type": "object", "required": [ "total_lines", "valid_count", "skipped_count" ], "title": "PreviewResponse", "description": "Response for ``GET /api/blocklists/{id}/preview``." }, "ReadyCheck": { "properties": { "name": { "type": "string", "title": "Name", "description": "Subsystem name." }, "healthy": { "type": "boolean", "title": "Healthy", "description": "True when the subsystem is operational." }, "message": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Message", "description": "Error detail when the check fails." } }, "type": "object", "required": [ "name", "healthy" ], "title": "ReadyCheck", "description": "Result of a single readiness subsystem check.\n\nFields:\n name: Subsystem name (e.g., \"database\", \"fail2ban\", \"config_dir\").\n healthy: True when the subsystem is reachable/operational.\n message: Optional error message describing the failure." }, "ReadyResponse": { "properties": { "status": { "type": "string", "enum": [ "ok", "error" ], "title": "Status", "description": "'ok' when all checks pass, 'error' when at least one fails." }, "checks": { "items": { "$ref": "#/components/schemas/ReadyCheck" }, "type": "array", "title": "Checks", "description": "Per-subsystem check results." }, "failed_count": { "type": "integer", "minimum": 0.0, "title": "Failed Count", "description": "Number of checks that returned healthy=False." } }, "type": "object", "required": [ "status", "failed_count" ], "title": "ReadyResponse", "description": "Structured readiness check response for the ``/health/ready`` endpoint.\n\nFields:\n status: \"ok\" when all checks pass, \"error\" when at least one failed.\n checks: Per-subsystem result list.\n failed_count: Number of checks that returned healthy=False.\n\nExample:\n ```python\n # All healthy (HTTP 200)\n {\"status\": \"ok\", \"checks\": [...], \"failed_count\": 0}\n\n # Some failed (HTTP 503)\n {\"status\": \"error\", \"checks\": [...], \"failed_count\": 2}\n ```" }, "RegexTestRequest": { "properties": { "log_line": { "type": "string", "title": "Log Line", "description": "Sample log line to test against." }, "fail_regex": { "type": "string", "title": "Fail Regex", "description": "Regex pattern to match." } }, "type": "object", "required": [ "log_line", "fail_regex" ], "title": "RegexTestRequest", "description": "Payload for ``POST /api/config/regex-test``." }, "RegexTestResponse": { "properties": { "matched": { "type": "boolean", "title": "Matched", "description": "Whether the pattern matched the log line." }, "groups": { "items": { "type": "string" }, "type": "array", "title": "Groups", "description": "Named groups captured by a successful match." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "Compilation error message if the regex is invalid." } }, "type": "object", "required": [ "matched" ], "title": "RegexTestResponse", "description": "Result of a regex test." }, "RollbackResponse": { "properties": { "jail_name": { "type": "string", "title": "Jail Name", "description": "Name of the jail that was disabled." }, "disabled": { "type": "boolean", "title": "Disabled", "description": "Whether the jail's .local override was successfully written with enabled=false." }, "fail2ban_running": { "type": "boolean", "title": "Fail2Ban Running", "description": "Whether fail2ban is online after the rollback attempt." }, "active_jails": { "type": "integer", "minimum": 0.0, "title": "Active Jails", "description": "Number of currently active jails after a successful restart.", "default": 0 }, "message": { "type": "string", "title": "Message", "description": "Human-readable result message." } }, "type": "object", "required": [ "jail_name", "disabled", "fail2ban_running", "message" ], "title": "RollbackResponse", "description": "Response for ``POST /api/config/jails/{name}/rollback``." }, "ScheduleConfig": { "properties": { "frequency": { "$ref": "#/components/schemas/ScheduleFrequency", "default": "daily" }, "interval_hours": { "type": "integer", "maximum": 168.0, "minimum": 1.0, "title": "Interval Hours", "description": "Used when frequency=hourly", "default": 24 }, "hour": { "type": "integer", "maximum": 23.0, "minimum": 0.0, "title": "Hour", "description": "UTC hour for daily/weekly runs", "default": 3 }, "minute": { "type": "integer", "maximum": 59.0, "minimum": 0.0, "title": "Minute", "description": "Minute for daily/weekly runs", "default": 0 }, "day_of_week": { "type": "integer", "maximum": 6.0, "minimum": 0.0, "title": "Day Of Week", "description": "Day of week for weekly runs (0=Monday \u2026 6=Sunday)", "default": 0 } }, "type": "object", "title": "ScheduleConfig", "description": "Import schedule configuration.\n\nThe interpretation of fields depends on *frequency*:\n\n- ``hourly``: ``interval_hours`` controls how often (every N hours).\n- ``daily``: ``hour`` and ``minute`` specify the daily run time (UTC).\n- ``weekly``: additionally uses ``day_of_week`` (0=Monday \u2026 6=Sunday)." }, "ScheduleFrequency": { "type": "string", "enum": [ "hourly", "daily", "weekly" ], "title": "ScheduleFrequency", "description": "Available import schedule frequency presets." }, "ScheduleInfo": { "properties": { "config": { "$ref": "#/components/schemas/ScheduleConfig" }, "next_run_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Next Run At" }, "last_run_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Last Run At" }, "last_run_errors": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "title": "Last Run Errors" } }, "type": "object", "required": [ "config", "next_run_at", "last_run_at" ], "title": "ScheduleInfo", "description": "Current schedule configuration together with runtime metadata." }, "SecurityHeadersResponse": { "properties": { "csrf_header_name": { "type": "string", "title": "Csrf Header Name", "description": "Name of the custom header required for state-mutating requests." }, "csrf_header_value": { "type": "string", "title": "Csrf Header Value", "description": "Required value of the CSRF header to pass validation." } }, "type": "object", "required": [ "csrf_header_name", "csrf_header_value" ], "title": "SecurityHeadersResponse", "description": "Security-relevant header names and values used by the frontend." }, "ServerSettings": { "properties": { "log_level": { "type": "string", "title": "Log Level", "description": "fail2ban daemon log level." }, "log_target": { "type": "string", "title": "Log Target", "description": "Log destination: STDOUT, STDERR, SYSLOG, or a file path." }, "syslog_socket": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Syslog Socket" }, "db_path": { "type": "string", "title": "Db Path", "description": "Path to the fail2ban ban history database." }, "db_purge_age": { "type": "integer", "title": "Db Purge Age", "description": "Seconds before old records are purged." }, "db_max_matches": { "type": "integer", "title": "Db Max Matches", "description": "Maximum stored matches per ban record." } }, "type": "object", "required": [ "log_level", "log_target", "db_path", "db_purge_age", "db_max_matches" ], "title": "ServerSettings", "description": "Domain model for fail2ban server-level settings." }, "ServerSettingsResponse": { "properties": { "settings": { "$ref": "#/components/schemas/ServerSettings" }, "warnings": { "additionalProperties": { "type": "boolean" }, "type": "object", "title": "Warnings", "description": "Warnings highlighting potentially unsafe settings." } }, "type": "object", "required": [ "settings" ], "title": "ServerSettingsResponse", "description": "Response for ``GET /api/server/settings``." }, "ServerSettingsUpdate": { "properties": { "log_level": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Log Level" }, "log_target": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Log Target" }, "db_purge_age": { "anyOf": [ { "type": "integer", "minimum": 0.0 }, { "type": "null" } ], "title": "Db Purge Age" }, "db_max_matches": { "anyOf": [ { "type": "integer", "minimum": 0.0 }, { "type": "null" } ], "title": "Db Max Matches" } }, "type": "object", "title": "ServerSettingsUpdate", "description": "Payload for ``PUT /api/server/settings``." }, "ServerStatus": { "properties": { "online": { "type": "boolean", "title": "Online", "description": "Whether fail2ban is reachable via its socket." }, "version": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Version", "description": "fail2ban version string." }, "active_jails": { "type": "integer", "minimum": 0.0, "title": "Active Jails", "description": "Number of currently active jails.", "default": 0 }, "total_bans": { "type": "integer", "minimum": 0.0, "title": "Total Bans", "description": "Aggregated current ban count across all jails.", "default": 0 }, "total_failures": { "type": "integer", "minimum": 0.0, "title": "Total Failures", "description": "Aggregated current failure count across all jails.", "default": 0 } }, "type": "object", "required": [ "online" ], "title": "ServerStatus", "description": "Cached fail2ban server health snapshot." }, "ServerStatusResponse": { "properties": { "status": { "$ref": "#/components/schemas/ServerStatus" } }, "type": "object", "required": [ "status" ], "title": "ServerStatusResponse", "description": "Response for ``GET /api/dashboard/status``." }, "ServiceStatusResponse": { "properties": { "online": { "type": "boolean", "title": "Online", "description": "Whether fail2ban is reachable via its socket." }, "version": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Version", "description": "BanGUI application version (or None when offline)." }, "jail_count": { "type": "integer", "minimum": 0.0, "title": "Jail Count", "description": "Number of currently active jails.", "default": 0 }, "total_bans": { "type": "integer", "minimum": 0.0, "title": "Total Bans", "description": "Aggregated current ban count across all jails.", "default": 0 }, "total_failures": { "type": "integer", "minimum": 0.0, "title": "Total Failures", "description": "Aggregated current failure count across all jails.", "default": 0 }, "log_level": { "type": "string", "title": "Log Level", "description": "Current fail2ban log level.", "default": "UNKNOWN" }, "log_target": { "type": "string", "title": "Log Target", "description": "Current fail2ban log target.", "default": "UNKNOWN" } }, "type": "object", "required": [ "online" ], "title": "ServiceStatusResponse", "description": "Response for ``GET /api/config/service-status``." }, "SessionValidResponse": { "properties": { "valid": { "type": "boolean", "title": "Valid", "description": "Whether the session is valid and active.", "default": true } }, "type": "object", "title": "SessionValidResponse", "description": "Response for ``GET /api/auth/session`` confirming session validity." }, "SetupRequest": { "properties": { "master_password": { "type": "string", "maxLength": 72, "minLength": 8, "title": "Master Password", "description": "Master password that protects the BanGUI interface (max 72 bytes due to bcrypt truncation)." }, "database_path": { "type": "string", "title": "Database Path", "description": "Filesystem path to the BanGUI SQLite application database.", "default": "bangui.db" }, "fail2ban_socket": { "type": "string", "title": "Fail2Ban Socket", "description": "Path to the fail2ban Unix domain socket.", "default": "/var/run/fail2ban/fail2ban.sock" }, "timezone": { "type": "string", "title": "Timezone", "description": "IANA timezone name used when displaying timestamps.", "default": "UTC" }, "session_duration_minutes": { "type": "integer", "minimum": 1.0, "title": "Session Duration Minutes", "description": "Number of minutes a user session remains valid.", "default": 60 } }, "type": "object", "required": [ "master_password" ], "title": "SetupRequest", "description": "Payload for ``POST /api/setup``." }, "SetupResponse": { "properties": { "message": { "type": "string", "title": "Message", "default": "Setup completed successfully. Please log in." } }, "type": "object", "title": "SetupResponse", "description": "Response returned after a successful initial setup." }, "SetupStatusResponse": { "properties": { "completed": { "type": "boolean", "title": "Completed", "description": "``True`` if the initial setup has already been performed." } }, "type": "object", "required": [ "completed" ], "title": "SetupStatusResponse", "description": "Response indicating whether setup has been completed." }, "SetupTimezoneResponse": { "properties": { "timezone": { "type": "string", "title": "Timezone", "description": "Configured IANA timezone identifier." } }, "type": "object", "required": [ "timezone" ], "title": "SetupTimezoneResponse", "description": "Response for ``GET /api/setup/timezone``." }, "UnbanAllResponse": { "properties": { "message": { "type": "string", "title": "Message", "description": "Human-readable summary of the operation." }, "count": { "type": "integer", "minimum": 0.0, "title": "Count", "description": "Number of IPs that were unbanned." } }, "type": "object", "required": [ "message", "count" ], "title": "UnbanAllResponse", "description": "Response for ``DELETE /api/bans/all``." }, "UnbanRequest": { "properties": { "ip": { "type": "string", "title": "Ip", "description": "IP address to unban." }, "jail": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Jail", "description": "Jail to remove the ban from. ``null`` means all jails." }, "unban_all": { "type": "boolean", "title": "Unban All", "description": "When ``true`` the IP is unbanned from every jail.", "default": false } }, "type": "object", "required": [ "ip" ], "title": "UnbanRequest", "description": "Payload for ``DELETE /api/bans`` (unban an IP)." }, "ValidationError": { "properties": { "loc": { "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "type": "array", "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" }, "ctx": { "type": "object", "title": "Context" } }, "type": "object", "required": [ "loc", "msg", "type" ], "title": "ValidationError" } } } }