# Python Tools This image is a Python 3 task runner with ExifTool and a small web control panel for running known, mounted Python maintenance tasks. It starts the control panel with `python -m app`. ## Control Panel The application discovers direct `*.py` files in each non-hidden directory under `/opt/tasks`. Each directory is a task group. Tasks in one group run sequentially; different groups may run concurrently. It records run history and combined output in SQLite under `/var/lib/server-maintenance`, and supports cancellation of active task process groups. The image contains application code only. Mount these paths at runtime: - `/opt/tasks` read-only: reviewed task scripts and their input manifests. - `/opt/credentials` read-only: named credential files used by those scripts. - `/var/lib/server-maintenance`: persistent SQLite data, logs, and task state. Task processes receive a minimal environment. To forward non-secret container settings to tasks, list their names in `SERVER_MAINTENANCE_TASK_ENV`, separated by commas. This allowlist prevents unrelated container configuration from being exposed to every task; credentials should remain in `/opt/credentials`. Tasks are never imported during discovery. Their optional description is the first line of their module docstring. A task must not require stdin or command line arguments, should write useful output, and must return a non-zero exit code on failure. ## Task inputs An optional `task-inputs.json` beside a group's task files declares the fields for its web forms and its task order. It is parsed as data only and uses version `1`: ```json { "version": 1, "label": "Example Tasks", "tasks": { "task.py": { "inputs": [ {"name": "identifier", "label": "Identifier", "type": "text", "required": true}, {"name": "attempts", "label": "Attempts", "type": "integer", "minimum": 1, "maximum": 5, "default": 2}, {"name": "mode", "label": "Mode", "type": "choice", "options": ["standard", "extended"]}, {"name": "alerts", "label": "Alerts", "type": "multi_choice", "options": ["email", "webhook"]} ] } } } ``` Supported field types are `text`, `integer`, `date`, `datetime`, `choice`, `multi_choice`, and `file`. Text may also define a regular-expression `pattern`; integer fields accept `minimum`, `maximum`, and `step`. Choices may use strings or objects with `value` and `label`. File fields accept a list of lowercase filename extensions in `accept` and a byte limit in `maximum_bytes`. An input can define `required` and `default`. Set `sensitive` to `true` for a value or upload that must not appear in run history. The form masks sensitive text, the database records `[redacted]`, and the original value is available only through a private execution input file. Tasks listed in the manifest appear in JSON order. A listed task may use `{}` when it only needs ordering; unlisted scripts appear afterward in alphabetical order. `label` is optional and overrides the group name displayed in the UI. Set `wait_for_result` to `true` for a short task whose form should wait for its final success or failure status. This disables the form and warns before leaving the page while the task is running; tasks without it keep the normal queued-run behavior. For a task with a review-first operation, declare `execution` with the input field and its review and execution values. The control panel generates the mode selector attached to the submit button. A successful review run offers a button that repeats the same saved inputs using the execution value; it does not reuse a stale filesystem snapshot, so the task must still validate its inputs before changing anything. The server validates every submitted value before a run is queued. It records the validated object in the run database and writes it to a per-run file. Sensitive fields are redacted in the database and use a private `0600` execution file instead. Tasks read the applicable file path from `SERVER_MAINTENANCE_INPUT`; no user value is appended to the command line. A task without an entry in the manifest gets an empty JSON object. Uploaded files are stored in a private per-run directory; their absolute paths are provided in the input JSON and the files are removed when the task finishes, fails, or is cancelled. ## Downloads Tasks can write downloadable files to the directory named by `SERVER_MAINTENANCE_ARTIFACTS`. Set `download_artifacts` to `true` alongside `wait_for_result` to start the first artifact download after a successful web run. Run-detail pages also show available downloads. The server deletes an artifact after its HTTP transfer ends; any undownloaded artifact is removed after 24 hours. A browser cannot confirm that a downloaded file was retained on the client device. ## Background tasks An optional `background-tasks.json` beside a group's task files declares internal recurring scripts. These scripts must live below the group's `internal/` directory, are not exposed in the UI, and are launched at most once per configured interval. Their latest output is retained below the persistent state directory rather than being added to normal run history. ```json { "version": 1, "tasks": [ {"id": "poll", "script": "internal/poll.py", "interval_seconds": 60} ] } ``` `compose.example.yaml` is suitable for adding the service to an existing Compose stack. Set `PYTHON_TOOLS_VOLUME_ROOT` to the host directory that will hold `state`, `tasks`, and `credentials`; set `SERVER_MAINTENANCE_ALLOWED_PROXY_IPS` to the reverse proxy's direct peer address. Keep the application behind an authenticated reverse proxy.