Funmill
Funmill provides one stable task API while execution is delegated to a
replaceable backend. The first backend is self-hosted
Windmill, pinned to v1.808.0.
client -> Funmill /v1 -> TaskBackend -> Windmill
-> another backend later
Funmill owns the public request and response models. Backend job IDs remain opaque strings, and no Windmill routes or payloads are exposed to clients.
Start
Install the project and the Windmill binary:
uv sync
uv run funmill install windmill
Start Windmill against an existing PostgreSQL database:
vim ~/.farfarfun/funmill/windmill/.env
uv run funmill start windmill
Set DATABASE_URL=postgresql://windmill:password@127.0.0.1:5432/windmill in
that file. The installer creates it with 0600 permissions and never
overwrites an existing configuration.
Open http://localhost:8813, log in with admin@windmill.dev / changeme,
change the password, and create an API token in the admins workspace. Then
start Funmill in another terminal:
FUNMILL_API_KEY='replace-me' \
WINDMILL_URL='http://127.0.0.1:8813' \
WINDMILL_WORKSPACE=admins \
WINDMILL_TOKEN='replace-me' \
uv run funmill start
The Funmill API port is fixed at 8812; the active third-party UI/API port is
fixed at 8813. OpenAPI docs are at http://localhost:8812/docs. All /v1
routes require X-API-Key. See the
Windmill deployment guide for
PostgreSQL setup and additional workers.
Run the end-to-end task and DAG checks with:
FUNMILL_API_KEY=the-value-from-env ./scripts/smoke.sh
Set FUNMILL_CALLBACK_URL to test callbacks. The URL must be reachable from
the Windmill workers.
API
| Operation | Route |
|---|---|
| Submit Python/Bash | POST /v1/tasks |
| Submit a DAG | POST /v1/workflows |
| Status | GET /v1/tasks/{task_id} |
| Logs | GET /v1/tasks/{task_id}/logs |
| Progress | GET /v1/tasks/{task_id}/progress |
| Result | GET /v1/tasks/{task_id}/result |
| Cancel | POST /v1/tasks/{task_id}/cancel |
| Rerun | POST /v1/tasks/{task_id}/rerun |
Submit one task, optionally waiting for existing task IDs:
{
"language": "python",
"source": "def main(value: int):\n return value * 2\n",
"args": {"value": 21},
"depends_on": ["EXISTING_TASK_ID"],
"dependency_timeout_seconds": 3600,
"retry": {"attempts": 2, "delay_seconds": 5},
"timeout_seconds": 300,
"callback_url": "https://example.internal/task-callback"
}
Submit A -> [B, C] as one workflow:
{
"tasks": [
{"key": "a", "language": "python", "source": "def main(): return 1"},
{"key": "b", "language": "python", "source": "def main(): return 2", "depends_on": ["a"]},
{"key": "c", "language": "bash", "source": "main() { echo 3; }", "depends_on": ["a"]}
]
}
Dependencies inside a workflow are task keys. Top-level depends_on values are
IDs returned by earlier Funmill submissions. The Windmill backend checks those
dependencies in short jobs separated by Flow sleeps, so waiting does not hold a
worker process. Failure or cancellation of a dependency fails the waiting task.
Workflow results and callback payloads are objects keyed by every workflow task
key. Each topological layer is a synchronization barrier; tasks in the same
layer run in parallel.
Callbacks contain task_id, status, and payload; success and failure
delivery retry three times. Delivery is at least once, so callback receivers
must be idempotent.
Backends
The public contract is TaskBackend in src/funmill/backends/base.py. Backend
selection uses FUNMILL_BACKEND; registration lives in
src/funmill/backends/__init__.py, following the same driver pattern as
fundrive. Each third-party adapter lives in its own directory, such as
src/funmill/backends/windmill/.
Changing the backend does not change /v1, but it does not migrate old jobs or
their IDs. Add a Funmill-owned ID mapping database only when jobs must remain
queryable after a live backend migration.
Operations
funmill services
funmill install windmill
funmill start windmill
funmill start
Both start commands run in the foreground and stop normally with Ctrl+C.
Use systemd or an existing process manager for long-running deployment. Add
TLS, PostgreSQL backups, callback egress restrictions, and a secrets manager
before network exposure.
Metadata
Release files for funmill 0.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| funmill-0.1.3-py3-none-any.whl | Python 3 | none | any | Details |
Release files / funmill-0.1.3-py3-none-any.whl
| Download URL | funmill-0.1.3-py3-none-any.whl |
|---|---|
| Size | 15.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fd16d04ac21a77ae408d9e30af063ef435f14f3a9327164db4de59275dca87e5
|
|
BLAKE2b-256 checksum How to use checksums |
ea8f1fd14e604660713aa94c1f02b1de7a905d1be5f60a640cf4c42fcba1e90b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for funmill 0.1.3
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.