A simple communication backend for scripts and other small applications. Patchwork enables IFTTT-type applications by providing infinite HTTP endpoints that serve as a multi-process, multi-consumer (MPMC) queue. Features include built-in metrics for monitoring, rate limiting for abuse prevention, and comprehensive authentication with token-based access control.
Patchwork provides infinite HTTP endpoints that can be used to implement powerful
serverless applications - including desktop notifications, SMS notifications,
job queues, web hosting, and file sharing. These applications are basically
just a few lines of bash that wrap a curl
command.
The philosophy behind this is that the main logic happens on the local machine with small scripts. There is a server with an infinite number of virtual channels that will relay messages between the publisher and the subscriber.
To subscribe to a channel you can simply make a GET
request:
curl http://patch.tionis.dev/public/queue/a61b1f42
The above will block until something is published to the channel a61b1f42
.
You can easily publish to a channel using a POST
request:
curl http://patch.tionis.dev/public/queue/a61b1f42 -d "hello, world"
The subscriber will immediately receive that data. If you reverse the order, then the post will block until it is received.
The default mode is a MPMC queue, where the first to connect are able to publish/subscribe. But you can also specify publish-subscribe (pubsub) mode. In pubsub mode, the publisher will become non-blocking and their data will be transmitted to each connected subscriber:
curl http://patch.tionis.dev/public/pubsub/a61b1f42 -d "hello, world"
Or use the flexible namespace with the pubsub parameter:
curl http://patch.tionis.dev/public/./a61b1f42?pubsub=true -d "hello, world"
You can also publish with a GET
request by using the parameter
body=X
, making it easier to write href links that can trigger hooks:
curl http://patch.tionis.dev/p/a61b1f42?pubsub=true&body=hello,%20world
Patchwork supports behavior determination based on the path structure, providing more predictable and explicit communication patterns:
Paths: /queue/...
or /./...
(default)
In queue mode, producers will block until a consumer is available to receive the data. This ensures one-to-one communication and guarantees message delivery.
# Producer blocks until consumer connects curl http://patch.tionis.dev/public/queue/jobs -d "process-file.txt" # Consumer receives the message curl http://patch.tionis.dev/public/queue/jobs
Paths: /pubsub/...
or /./...?pubsub=true
In pubsub mode, producers send data immediately and don't wait for consumers. All connected consumers receive the same message (broadcast).
# Producer sends immediately (non-blocking) curl http://patch.tionis.dev/public/pubsub/events -d "user-login" # Multiple consumers can receive the same event curl http://patch.tionis.dev/public/pubsub/events # Consumer 1 curl http://patch.tionis.dev/public/pubsub/events # Consumer 2
Paths: /./...
The flexible space defaults to queue behavior but can be switched to pubsub with the ?pubsub=true
query parameter:
# Default: queue behavior (blocking) curl http://patch.tionis.dev/public/./notifications -d "alert" # Override: pubsub behavior (non-blocking) curl http://patch.tionis.dev/public/./notifications?pubsub=true -d "broadcast"
Paths: /req/...
and /res/...
subnamespaces
The request-responder pattern enables HTTP request/response communication through the patchwork relay. Requesters send to /req/...
paths, and responders handle them via /res/...
paths with the same subpath.
Requesters and responders use matching subpaths. For example, /req/api/users
pairs with /res/api/users
.
# Responder waits for requests curl http://patch.tionis.dev/public/res/api/users # Requester sends a GET request (blocks until response) curl http://patch.tionis.dev/public/req/api/users
All HTTP methods are supported (GET, POST, PUT, DELETE, PATCH, etc.). The method is preserved from requester to responder.
# Responder handles various methods curl http://patch.tionis.dev/public/res/api/users # POST request with data curl -X POST -d '{"name":"Alice"}' http://patch.tionis.dev/public/req/api/users # PUT request curl -X PUT -d '{"id":1,"name":"Bob"}' http://patch.tionis.dev/public/req/api/users # DELETE request curl -X DELETE http://patch.tionis.dev/public/req/api/users/1
Use ?switch=true
on the responder side to enable "double clutch" mode. In this mode, the responder receives the request, then switches to a new channel to send the response. This allows for dynamic response routing.
# Terminal 1: Start requester (will wait for response) curl -X POST -d '{"data":"request"}' http://patch.tionis.dev/public/req/myservice # Terminal 2: Responder in switch mode # Body contains the new channel ID to switch to curl -X POST "http://patch.tionis.dev/public/res/myservice?switch=true" -d "new-channel-123" # This returns the request data to the responder # Terminal 3: Send the actual response on the new channel curl -X POST -d '{"result":"processed"}' http://patch.tionis.dev/public/new-channel-123 # The original requester receives this response
Patchwork supports passthrough headers using the Patch-H-*
prefix system, allowing you to forward original request context between clients through the relay system.
Patch-H-
represent original headers from the requesterPatch-H-*
headers are stripped of their prefix and passed through to the final receiverUser-Agent
, Accept
, etc. are automatically converted to Patch-H-*
formatProducer side (sending headers):
curl -X POST \ -H "Patch-H-Original-IP: 192.168.1.100" \ -H "Patch-H-User-ID: alice123" \ -H "User-Agent: MyApp/1.0" \ -d "request data" \ http://patch.tionis.dev/public/queue/api
Consumer side (receiving headers):
curl -v http://patch.tionis.dev/public/queue/api # Response includes: # Original-IP: 192.168.1.100 # User-ID: alice123 # User-Agent: MyApp/1.0
This enables building proxy-like applications where the original request context is preserved through the relay.
In request-responder mode, additional headers provide request context and response control:
/api/users/123
)404
, 201
)Patch-H-*
headers work as normal for forwarding request contextRequest-responder example:
# Responder receives request with context curl http://patch.tionis.dev/public/res/api/users # Request headers include: # Patch-Uri: /api/users/123 # Patch-H-Authorization: Bearer token123 # Responder can set status in response curl -H "Patch-Status: 201" -d '{"created": true}' http://patch.tionis.dev/public/res/api/users
The server is organized by namespaces with different access patterns. Each namespace now supports structured sub-paths that determine the communication behavior:
All namespaces (public, user, etc.) now support the following sub-paths:
?pubsub=true
/h
to obtain a new channel and secret,
then use the secret to POST data to that channel. Anyone can GET data from the channel.
Useful for webhooks and notifications where you want to control who can send.
/r
to obtain a new channel and secret,
then anyone can POST data to that channel. Use the secret to GET data from the channel.
Useful for collecting data from multiple sources where you want to control who can read.
/_/ntfy
) for sending alerts and messages.
Authorization
header. Tokens are managed through
a config.yaml
file in the user's .patchwork
repository.
For user namespaces (/u/{username}/
), access is controlled by tokens
that you configure in your own .patchwork
repository. Create a
.patchwork
repository in your account and add a config.yaml
file to define tokens, permissions, notification settings, and HuProxy access.
If a request is made to a user namespace without an Authorization
header or token
query parameter, it is treated as a request with the token public
. This allows for creating public endpoints within a user's namespace that can be accessed without authentication.
Create a config.yaml
file in your .patchwork
repository:
tokens: "my_token_name": is_admin: false POST: - "/projects/*/logs" # Can POST to any project's logs - "/webhooks/*" # Can POST to webhook endpoints - "/_/ntfy" # Can send notifications GET: - "/projects/myapp/**" # Can GET from all myapp paths - "!/projects/myapp/secret/**" # But not from secret paths huproxy: - "*.example.com:*" # Can access specific hosts via HuProxy - "localhost:*" "readonly_token": is_admin: false POST: [] # No POST access GET: - "*" # Can GET from all paths "admin_token": is_admin: true POST: ["*"] GET: ["*"] # Public token allows unauthenticated access to specific endpoints "public": is_admin: false GET: - "/status" # Allow public status checks - "/health" # Allow public health checks POST: [] # No public POST access # Optional: Configure notification backend ntfy: type: matrix config: access_token: "your_matrix_access_token" user: "@bot:matrix.org" endpoint: "https://matrix.org" room_id: "!roomid:matrix.org"
Use glob patterns to control access: *
matches everything,
empty array []
denies access, and negation patterns like
!secret/**
can exclude specific paths.
Public Token Behavior: When no token is provided, the request is treated as using the "public" token. This allows users to create public endpoints within their namespace that can be accessed without authentication.
Example token usage:
# Authenticated access curl http://patch.tionis.dev/u/alice/projects/logs?token=my_token_name -d "Deploy completed" # Public access (no token needed, uses "public" token) curl http://patch.tionis.dev/u/alice/status
Send notifications via the /_/ntfy
endpoint:
# JSON notification curl -X POST "http://patch.tionis.dev/u/alice/_/ntfy" \ -H "Authorization: Bearer my_token_name" \ -H "Content-Type: application/json" \ -d '{"type": "markdown", "title": "Alert", "message": "Something **important** happened!"}' # Plain text notification curl -X POST "http://patch.tionis.dev/u/alice/_/ntfy" \ -H "Authorization: Bearer my_token_name" \ -H "Content-Type: text/plain" \ -d "Server maintenance completed"
Patchwork provides comprehensive monitoring capabilities through Prometheus-compatible metrics:
The /metrics
endpoint exposes server metrics with built-in authentication:
# Local monitoring (no auth needed) curl http://patch.tionis.dev/metrics # Remote monitoring with authentication curl -H "Authorization: Bearer your-forgejo-token" http://patch.tionis.dev/metrics # Using query parameter curl http://patch.tionis.dev/metrics?token=your-forgejo-token
patchwork_http_requests_total
- Total HTTP requests by method, namespace, statuspatchwork_http_request_duration_seconds
- Request duration histogramspatchwork_channels_total
- Current number of active channelspatchwork_active_connections
- Active WebSocket/long-polling connectionspatchwork_messages_total
- Messages processed by namespace and behaviorpatchwork_auth_requests_total
- Authentication attempts by resultpatchwork_cache_operations_total
- Cache hit/miss statisticsStandard health check endpoints for service monitoring:
# Health check endpoints curl http://patch.tionis.dev/healthz curl http://patch.tionis.dev/status
Each endpoint supports multiple modes:
Queue behavior (one-to-one, blocking):
# Producer waits until consumer connects curl http://patch.tionis.dev/public/queue/jobs -d "encode-video.mp4" # Consumer receives the job curl http://patch.tionis.dev/public/queue/jobs
Pubsub behavior (broadcast, non-blocking):
# Producer sends immediately to all consumers curl http://patch.tionis.dev/public/pubsub/events -d "user-login:alice" # Multiple consumers can listen curl http://patch.tionis.dev/public/pubsub/events # Logger service curl http://patch.tionis.dev/public/pubsub/events # Analytics service
Flexible behavior with override:
# Default blocking behavior curl http://patch.tionis.dev/public/./alerts -d "server-down" # Override to pubsub curl http://patch.tionis.dev/public/./alerts?pubsub=true -d "system-update"
Using passthrough headers:
# Send with context headers curl -X POST \ -H "Patch-H-Client-IP: 10.0.1.5" \ -H "Patch-H-Trace-ID: req-12345" \ -d "api-request" \ http://patch.tionis.dev/public/queue/api # Receive with original context curl -v http://patch.tionis.dev/public/queue/api # Headers include: Client-IP: 10.0.1.5, Trace-ID: req-12345
Request-responder pattern:
# Responder waits for API requests curl http://patch.tionis.dev/public/res/api/users # Requester sends GET request (blocks until response) curl http://patch.tionis.dev/public/req/api/users # POST request with data and headers curl -X POST \ -H "Authorization: Bearer token123" \ -d '{"name":"Alice"}' \ http://patch.tionis.dev/public/req/api/users # Responder can set status code in response curl -H "Patch-Status: 201" \ -d '{"id":123,"name":"Alice"}' \ http://patch.tionis.dev/public/res/api/users # Double clutch mode for multiple requests curl "http://patch.tionis.dev/public/res/api/process?switch=true"
Sending a file:
curl -X POST --data-binary "@test.txt" http://patch.tionis.dev/public/queue/files
Receiving a file:
curl http://patch.tionis.dev/public/queue/files > test.txt
#!/bin/bash MAGIC="notify" URL="http://patch.tionis.dev/p/notifications" while [ 1 ] do X="$(curl $URL)" if [[ $X =~ ^$MAGIC ]]; then Y="$(echo "$X" | sed "s/$MAGIC*//")" notify-send "$Y" else sleep 10 fi done
Adding jobs to a queue:
#!/bin/bash for filename in *.mp3 do curl http://patch.tionis.dev/p/jobs -d $filename done
Processing jobs from the queue:
#!/bin/bash while true do filename=$(curl -s http://patch.tionis.dev/p/jobs) if [ "$filename" != "Too Many Requests" ] then echo "Processing: $filename" # Process the file here ffmpeg -i "$filename" "$filename.ogg" else sleep 1 fi done
To use forward hooks, first obtain a channel and secret:
# Get a new channel and secret curl http://patch.tionis.dev/h # Returns: {"channel":"abc123-def456-...","secret":"sha256hash..."} # Send notification (requires secret) curl http://patch.tionis.dev/h/abc123-def456-...?secret=sha256hash... -d "Server is down!" # Anyone can listen for notifications curl http://patch.tionis.dev/h/abc123-def456-...
Similarly, for reverse hooks, obtain a channel and secret:
# Get a new channel and secret curl http://patch.tionis.dev/r # Returns: {"channel":"xyz789-abc123-...","secret":"sha256hash..."} # Anyone can submit metrics curl http://patch.tionis.dev/r/xyz789-abc123-... -d "cpu:85%" curl http://patch.tionis.dev/r/xyz789-abc123-... -d "memory:67%" # Reading requires secret curl http://patch.tionis.dev/r/xyz789-abc123-...?secret=sha256hash...
Note: The secrets are generated using HMAC-SHA256 with a server secret key and the channel name.
Set the SECRET_KEY
environment variable to ensure secrets persist across server restarts.
The huproxy endpoint allows you to tunnel SSH and other TCP protocols through HTTP/HTTPS, useful when direct connections are blocked by firewalls.
Configure your tokens in your .patchwork/config.yaml
file:
tokens: "your_secure_token": huproxy: - "*" # Allow all hosts and ports - "*.example.com:*" # Or restrict to specific patterns
# Using with SSH and a WebSocket client ssh -o 'ProxyCommand=huproxyclient -auth=Bearer:your_token ws://patch.tionis.dev/huproxy/alice/targethost/22' user@targethost # Testing connectivity curl -H "Authorization: Bearer your_token" \ --http1.1 \ --upgrade websocket \ http://patch.tionis.dev/huproxy/alice/localhost/22
You can download a bash based client here.