artur
artur is a config-driven Rust HTTP server.
It lets developers expose HTTP endpoints from TOML and attach each endpoint to a controlled action. The core server stays generic: it does not hardcode challenges, wallets, blockchains, databases, queues, or business workflows. Those belong in commands or scripts that artur starts from configuration.
Or run the container image published by GitHub Actions to GitHub Packages:
The default example listens on 127.0.0.1:46796:
What Artur does
Artur maps configured HTTP routes to generic actions:
| Action | Purpose |
|---|---|
respond.static |
Return configured JSON for health checks, metadata, mocks, or docs endpoints. |
process.run |
Run an allowlisted process synchronously or asynchronously. |
job.get |
Read the status and result of an async process started by process.run. |
A process can be a Python script, Rust CLI, shell script, npx command, binary in your repo, or any executable available to the service user.
Minimal configuration
= 1
[]
= "127.0.0.1"
= 46796
= 1048576
[[]]
= "hello"
= "GET"
= "/v1/hello"
= "respond.static"
[]
= 200
= { = true, = "artur" }
Run it:
Process endpoint
[[]]
= "echo"
= "POST"
= "/v1/process/echo/{name}"
= "process.run"
= "echo_json"
[[]]
= "echo_json"
= "sync"
= "python3"
= ["examples/scripts/echo.py", "--name", "{{param.name}}", "--source", "{{query.source}}"]
= 30000
= "json"
[]
= "request_json"
Call it:
The process receives a request JSON document on stdin:
Async process endpoint
[[]]
= "long_task"
= "POST"
= "/v1/process/long-task"
= "process.run"
= "long_task"
[[]]
= "long_task"
= "async"
= "python3"
= ["examples/scripts/long_task.py"]
= 60000
= "json"
[]
= "body"
[[]]
= "get_job"
= "GET"
= "/v1/jobs/{job_id}"
= "job.get"
Starting the task returns a job ID:
Then read it:
Current jobs are stored in memory. Use an external process or service when you need durable queues, retries, distributed workers, or long-lived state.
Template variables
Artur renders templates inside process args, env values, working directories, and stdin.type = "template" payloads.
| Template | Value |
|---|---|
{{method}} |
HTTP method. |
{{uri}} |
Full request URI. |
{{path}} |
Request path without query string. |
{{body}} |
Raw request body as text. |
{{request_json}} or {{request}} |
Full request context as compact JSON. |
{{param.name}} |
Path parameter, such as {name}. |
{{query.name}} |
Query string value. |
{{header.name}} |
Header value, lower-case lookup. |
{{env.NAME}} |
Environment variable from the Artur process. |
{{body_json.user.id}} |
Field inside a JSON request body. Array indexes are supported, such as {{body_json.items.0}}. |
Unknown template keys render as an empty string.
Process stdin modes
[]
= "none"
[]
= "body"
[]
= "request_json"
[]
= "template"
= "user={{body_json.user.id}}"
Challenge and space example
examples/challenge-space.toml shows how to model this kind of flow without hardcoding it into Artur:
POST /v1/challengeruns an externalchallenge create ...command.POST /v1/spaceruns your application script, which can callchallenge verify ..., allocate a randomsid, assign resources, persist data, and return JSON.GET /v1/space/{sid}andGET /v1/space/delegate lookup to your application script.
This is only an example of how developers can work with Artur. ALTCHA-style challenges, wallets, blockchains, balances, deposits, and expenses are application concerns, not Artur core concepts.
If your preferred challenge crate exposes a binary named challenge, this kind of TOML can call it directly:
[[]]
= "challenge_create"
= "sync"
= "challenge"
= [
"create",
"--cost", "5000",
"--random-counter",
"--expires-in", "600",
"--hmac-secret", "{{env.ARTUR_CHALLENGE_HMAC_SECRET}}",
"--hmac-key-secret", "{{env.ARTUR_CHALLENGE_HMAC_KEY_SECRET}}",
]
= "json"
The important part is that a developer can swap this for any other implementation by changing TOML, not recompiling Artur.
Security and operations notes
- Treat the TOML file as privileged code. Anyone who can edit it can define which commands Artur runs.
- Prefer absolute command paths in production.
- Keep
timeout_mssmall and specific per process. - Avoid placing secrets in command-line args because they may be visible in process listings. Prefer env vars and short-lived credentials.
- Run Artur as an unprivileged service user.
- Use a reverse proxy for TLS, authentication, rate limiting, and request logging when exposing Artur outside localhost.
- Use async process mode for long tasks and external durable queues when results must survive restarts.
Development
Prerequisites:
- Rust stable matching
rust-versioninCargo.toml. - Node.js/npm for the JavaScript and local
npxe2e coverage. - Docker for container build verification.
Run the same core checks as CI:
The e2e test suite starts the compiled artur binary and verifies configured static endpoints plus process endpoints backed by JavaScript (node), local npx, and a compiled Rust helper.
Docker
The included Dockerfile builds a minimal runtime image with the artur binary and example configuration. The container uses examples/docker.toml, which binds to 0.0.0.0:46796 for Docker port publishing.
GitHub Actions builds the image on pull requests and publishes it to GitHub Packages (ghcr.io/melonask/artur) on pushes to the default branch and on releases.
License
- MIT license, in
LICENSE