RTask is a secure task runner with API key management that exposes system commands as HTTP endpoints. It provides authenticated access to configured tasks with rate limiting, metrics collection, and secure API key management.
- Secure API Key Management: Argon2-based key hashing with CBOR encoding
- Rate Limiting: Configurable per-task rate limiting to prevent abuse
- Metrics Collection: Prometheus metrics for monitoring task execution and rejections
- Task Configuration: TOML-based configuration for defining tasks and their permissions
- Concurrent Safety: Mutex-based locking to prevent concurrent task execution
- Flexible Execution: Support for both blocking and non-blocking task execution
- Go 1.25 or later
- Optional: just for build automation
git clone <repository-url>
cd rtask
go buildOr using just:
just buildRTask uses TOML configuration files to define tasks and API key settings.
[tasks.hello]
command = ["./test.nu"]
apiKeyNames = ["a1", "a3"]
async = false
rateLimit = 1.0
[tasks.build]
command = ["make", "build"]
workdir = "/path/to/project"
apiKeyNames = ["github-action"]
async = true
asyncResultRetentionSeconds = 120 # Keep results for 2 minutes
rateLimit = 0.5
maxConcurrentTasks = 2
executionTimeoutSeconds = 300
[tasks.webhook-handler]
command = ["/usr/local/bin/process-webhook.sh"]
async = true
passRequestHeaders = ["X-GitHub-Event", "X-Hub-Signature"]
webhookSecrets.github = "your-webhook-secret-hash"
webhookSecretFiles.gitlab = "/etc/rtask/gitlab-webhook-secret"
[tasks.webhook-handler.environment]
LOG_LEVEL = "info"
WEBHOOK_TIMEOUT = "30"command: Array of command and arguments to executeworkdir: Working directory for command execution (optional)apiKeyNames: List of API key names that can access this task via/tasks/{task-name}endpointwebhookSecrets: Map of user-friendly names to webhook secret hashes for webhook authenticationwebhookSecretFiles: Map of user-friendly names to file paths containing webhook secret hashesenvironment: Map of environment variables to pass to the taskpassRequestHeaders: List of HTTP request headers to pass as environment variables (e.g.,X-Custom-HeaderbecomesREQUEST_HEADER_X_CUSTOM_HEADER)async: If true, returns task ID immediately and runs in background; if false, waits for completion and returns resultasyncResultRetentionSeconds: How long to keep async task results before cleanup in seconds (default: 60)rateLimit: Requests per second (default: 0 = unlimited)maxConcurrentTasks: Maximum number of concurrent task executions (default: 0 = unlimited)maxInputBytes: Maximum input size in bytes (default: 16KB)maxOutputBytes: Maximum output size in bytes (default: 16KB)executionTimeoutSeconds: Task execution timeout in seconds (default: 30)mergeStderr: If true, merge stderr into stdout (default: false, keep separate)durationHistogramBuckets: Custom histogram buckets for task duration metrics
# Start with default configuration
./rtask run
# Start with custom configuration and addresses
./rtask run --config ./my-config.toml --api-address localhost:8080 --metrics-address localhost:9091# Add a new API key
./rtask add-key my-key-nameThis will output an API key that can be used to authenticate requests.
For tasks configured with async = false, the response contains the execution result:
curl -X POST \
-H "Authorization: Bearer YOUR_API_KEY" \
-d "input data" \
http://localhost:8800/tasks/hello
# Response:
# {
# "status": "success",
# "exit_code": 0,
# "stdout": "task output",
# "stderr": ""
# }For tasks configured with async = true, you receive a task ID immediately:
# Submit task
curl -X POST \
-H "Authorization: Bearer YOUR_API_KEY" \
-d "input data" \
http://localhost:8800/tasks/build
# Response: {"task_id": "kj4w2ndvmfzxg2lk"}
# Poll for result
curl -H "Authorization: Bearer YOUR_API_KEY" \
http://localhost:8800/tasks/build/kj4w2ndvmfzxg2lk
# Response when complete:
# {
# "status": "success",
# "exit_code": 0,
# "stdout": "build output",
# "stderr": ""
# }For tasks configured with webhooks, no API key is required:
curl -X POST \
-d "webhook payload" \
http://localhost:8800/wh/your-webhook-hashPrometheus metrics are available at the metrics endpoint:
curl http://localhost:9090/metricsAvailable metrics:
task_duration_seconds: Histogram of task execution timestask_rejection_total: Counter of rejected requests by reason
POST /tasks/{task-name}: Execute the specified task with API key authentication- Synchronous mode (
async = false): Returns task execution result immediately - Async mode (
async = true): Returns{"task_id": "..."}immediately
- Synchronous mode (
GET /tasks/{task-name}/{task-id}: Retrieve async task result by task ID- Returns
404if task not found (never existed or cleaned up after retention period) - Task results are retained for a configurable period after completion (default: 60 seconds)
- Configure retention with
asyncResultRetentionSecondsin task config
- Returns
POST /wh/{webhook-hash}: Execute task via webhook (no API key required)- Webhook hash is derived from
webhookSecretsorwebhookSecretFilesconfiguration - Key name is automatically set in the context for metrics
- Webhook hash is derived from
GET /metrics: Prometheus metrics endpoint
- API Key Hashing: Keys are hashed using Argon2 with configurable parameters
- Bearer Token Authentication: Standard HTTP Bearer token authentication
- Rate Limiting: Per-task rate limiting to prevent abuse
- Concurrent Execution Control: Mutex locking prevents multiple simultaneous executions
- Secure Key Storage: API keys are stored in TOML files with restricted permissions (0600)
401 Unauthorized: Missing or invalid API key429 Too Many Requests: Rate limit exceeded or task already running500 Internal Server Error: Task execution failed
Run all tests:
go test -vRun specific test:
go test -v -run TestSyncTask_SuccessRun tests with timeout:
go test -v -timeout 3mGO_LOG=debug ./rtask run# Build the project
just build
# Run with debug logging
just run run --config config.toml