Blanket is a RESTy wrapper for long running tasks.
Two options — pick one.
Native (Ubuntu / WSL2) — installs Go, Node, and Playwright locally
(uses sudo for apt + /usr/local/go). See scripts/setup.sh:
make setupDocker — reproducible toolchain image; the same image CI will run. No local Go or Node install needed:
make docker-test # Go unit tests in the container
make docker-test-browser # Playwright suite
make docker-test-smoke # binary smoke test
make docker-shell # interactive shell, source mounted at /srcSee Dockerfile for what the image carries.
# Build the binary. `make linux` → ./blanket-linux-amd64,
# `make darwin` → ./blanket-darwin-amd64, `make windows` → .exe.
make linux
# Copy the shipped example task types so there's something to exercise.
# Drop your own TOML files in this directory (or any directory listed in
# `tasks.typesPaths`) as you build up your own types.
cp -r examples/types ./types
# Create a config file
cat > blanket.json <<'EOF'
{
"port": 8773,
"tasks": {
"typesPaths": ["./types"],
"resultsPath": "results"
},
"logLevel": "debug"
}
EOF
# Run server
./blanket-linux-amd64Once the server is running, you can view the web UI at http://localhost:8773/. You can also interact with blanket via curl and the command line. For example, you can list tasks
curl -s -X GET localhost:8773/task/ | jq .
# OR
./blanket-linux-amd64 psSubmit a task of the shipped echo_task type (the minimal one — just
writes a string to stdout):
curl -s -X POST localhost:8773/task/ \
-d '{"type": "echo_task"}'The bash_task example takes a DEFAULT_COMMAND env var and runs it
through bash — the generic escape hatch for ad-hoc commands:
curl -s -X POST localhost:8773/task/ \
-d '{"type": "bash_task", "environment": {"DEFAULT_COMMAND": "echo $(date)"}}'
curl -X POST localhost:8773/task/ \
-d '{"type": "bash_task", "environment": {"DEFAULT_COMMAND": "cd ~ && ls -lah"}}'The python_hello example shells out to python3 and has an optional
NAME env var with a default:
curl -s -X POST localhost:8773/task/ \
-d '{"type": "python_hello", "environment": {"NAME": "blanket"}}'See examples/types/*.toml for the schema; drop your own TOML files
into ./types/ and submit them the same way.
Add a task and upload some data files with it. Files will be placed at the root of the directory where the task runs.
# Send task as a form field named data with multiple files
curl -X POST localhost:8773/task/ \
-F data='{"type": "echo_task", "environment": {"GREETING": "hi"}}' \
-F blanket.json=@blanket.json
# Send task data as another file named "data"
cat > data.json <<'EOF'
{
"type": "echo_task",
"environment": {
"GREETING": "hi"
}
}
EOF
curl -X POST localhost:8773/task/ -F data=@data.json -F blanket.json=@blanket.jsonAdd lots of tasks
while true; do
curl -X POST localhost:8773/task/ \
-F data=@data.json \
-F blanket.json=@blanket.json
echo "$(date)"
sleep 5
doneThere is also limited functionality for sending tasks via the command line.
# Send in a single task
$ ./blanket-linux-amd64 submit -t echo_task -e '{"GREETING": "hi"}'
echo_task 69ded2acce42aa8a11ac9ddc [1744748400]
# Send in a single task, printing only the id of the task once it is submitted
$ ./blanket-linux-amd64 submit -t echo_task -e '{"GREETING": "hi"}' -q
69ded2adce42aa8a11ac9de0
Delete a task
curl -s -X DELETE localhost:8773/task/69ded2acce42aa8a11ac9ddc | jq .
# OR
./blanket-linux-amd64 ps -q | tail -n1 | xargs ./blanket-linux-amd64 rm
# Remove all tasks
./blanket-linux-amd64 ps -q | xargs -I {} ./blanket-linux-amd64 rm {}Run worker with certain capabilities
# You can also launch from the web UI or via an api call
./blanket-linux-amd64 worker -t unix,bash,pythongo build produces a single static binary with the web UI baked in.
Templates, CSS, and vendored htmx live under server/ui_next/ and are
pulled into the binary via //go:embed (see server/ui_next.go). No
separate asset deploy, no runtime filesystem lookups — drop the binary
on a host and run it.
To refresh the vendored htmx bundle:
curl -sSfL https://unpkg.com/htmx.org@1.9.12/dist/htmx.min.js \
-o server/ui_next/static/htmx.min.js
curl -sSfL https://unpkg.com/htmx.org@1.9.12/dist/ext/sse.js \
-o server/ui_next/static/htmx-sse.js$ ./blanket-linux-amd64 -h
A fast and easy way to wrap applications and make them available via nice clean REST interfaces with built in UI, command line tools, and queuing, all in a single binary!
Usage:
blanket [flags]
blanket [command]
Available Commands:
completion Generate the autocompletion script for the specified shell
help Help about any command
ps List active and queued tasks
rm Remove tasks
submit Submit a task to be executed.
version Print the version number of blanket
worker Run a worker with capabilities defined by tags
Flags:
-c, --config string config file (default is blanket.yaml|json|toml)
-h, --help help for blanket
--logLevel string the logging level to use (default "info")
-p, --port int32 Port the server will run on (default 8773)
Use "blanket [command] --help" for more information about a command.See the docs directory for more detailed information about the design of blanket.
Blanket was designed because of problems I saw on several projects in GTRI's ELSYS branch where we wanted to be able to integrate a piece of software with a less than awesome API into another tool. Whether that software was a long running simulation, a CAD renderer, or some other strange beast, we kept seeing people try to wrap HTTP servers around these utilities. This seemed unnecessary and wasteful.
The starting concept of blanket was simple: If we can wrap anything with a command line call (which is possible with tools like sikuli), and we could make it easy to expose any command line script as a web endpoint, then we can provide a nice consistent way to expose cool software with a possibly bad API to a larger class of users.
The first draft of blanket was written in python and used celery for queuing. It worked fine, but was a bit heavy weight, and was hard for some Windows users to install. Go was chosen for the rewrite since
- It compiles to a single binary, so deployment is easy
- It cross compiles to many platforms, so getting it to behave on windows shouldn't be too painful
This was my first major project in Go, and the code base is still recovering from some early "experiments". It is still a bit rough around the edges, but I do use blanket almost every day at work to manage long running tasks that I'd like to keep a record of, like code deployments. In this regard, it's a bit like a light-weight jenkins. I plan to continue working on blanket, and I expect it will become a major component of many of my future side projects.
If you are interested in using blanket for a project and want to ask whether blanket may be a good match, you can either submit a github issue with your question or find me on twitter @turtlemonvh.
This is how we want it to work, not necessarily how it works now.
- Speed is not a high priority at the moment. Instead, we favor
- simplicity: API is easy to work with, and tasks are hard to lose
- pluggability: It is easy to change storage and queue backends while maintaining the same API.
- traceable: It's easy to understand what's going on.
- open: It's easy to get data in and out.
- low resourse usage: Like xinetd, it can be present and usable without you thinking about it.
- Blanket is designed for long running tasks, not high speed messaging. We assume
- Tasks will be running for a long time (several seconds or more).
- Contention between workers will be fairly low.
- TOML files drive all configuration for tasks
- The web UI is optional
- Everything can be done without it, easily
- The main feature is a json/rest interface
- Go modules for dependency management
//go:embedfor static files (seeserver/ui_next.go)- Server-rendered Go templates + htmx for the web UI