{"id":51600015,"url":"https://github.com/melonask/artur","last_synced_at":"2026-07-11T21:02:26.610Z","repository":{"id":364831698,"uuid":"1268729067","full_name":"melonask/artur","owner":"melonask","description":"Config-driven Rust HTTP server.","archived":false,"fork":false,"pushed_at":"2026-06-14T16:55:32.000Z","size":107,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-30T20:05:04.875Z","etag":null,"topics":["http","http-server","rust","rust-lang"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/artur","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/melonask.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-13T21:44:11.000Z","updated_at":"2026-06-14T16:55:35.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/melonask/artur","commit_stats":null,"previous_names":["melonask/artur"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/melonask/artur","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/melonask%2Fartur","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/melonask%2Fartur/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/melonask%2Fartur/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/melonask%2Fartur/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/melonask","download_url":"https://codeload.github.com/melonask/artur/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/melonask%2Fartur/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35375153,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-11T02:00:05.354Z","response_time":104,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["http","http-server","rust","rust-lang"],"created_at":"2026-07-11T21:02:23.481Z","updated_at":"2026-07-11T21:02:26.604Z","avatar_url":"https://github.com/melonask.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# artur\n\n\u003cimg align=\"right\" src=\"https://raw.githubusercontent.com/melonask/artur/refs/heads/main/logo.svg\" alt=\"Artur is a config-driven Rust HTTP server\" width=\"200\" /\u003e\n\n`artur` is a universal config-driven Rust HTTP gateway and package orchestrator.\n\nIt 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.\n\n```bash\ncargo install artur\nartur --config http://example.com/config.toml\n```\n\nOr run the container image published by GitHub Actions to GitHub Packages:\n\n```bash\ndocker run --rm -p 46796:46796 ghcr.io/melonask/artur:latest\ncurl -sS http://127.0.0.1:46796/v1/hello\n```\n\nThe default example listens on `127.0.0.1:46796`:\n\n```bash\nartur --config examples/config.toml\nartur --config examples/config.toml --port 46796\n```\n\n## What Artur does\n\nArtur maps configured HTTP routes to generic actions under the `[artur]` namespace:\n\n| Action | Purpose |\n| --- | --- |\n| `respond.static` | Return configured JSON for health checks, metadata, mocks, or docs endpoints. |\n| `task.run` | Run an allowlisted task synchronously or asynchronously. |\n| `job.get` | Read the status and result of an async task started by `task.run`. |\n| `workflow.run` | Run a DAG of task, store query, store execute, and response steps with dependency-based sequential or parallel execution. |\n\nA task can be a Python script, Rust CLI, shell script, `npx` command, binary in your repo, or any executable available to the service user. For distributed deployments, workflow steps can also call already-running package services through shared `[transports.http.*]` profiles.\n\n## Minimal configuration\n\n```toml\nversion = 1\n\n[artur.server]\nbind = \"127.0.0.1\"\nport = 46796\nbody_limit_bytes = 1048576\n\n[[artur.endpoints]]\nname = \"hello\"\nmethod = \"GET\"\npath = \"/v1/hello\"\naction = \"respond.static\"\n\n[artur.endpoints.response]\nstatus = 200\nbody = { ok = true, service = \"artur\" }\n```\n\nRun it:\n\n```bash\nartur --config examples/config.toml\ncurl -sS http://127.0.0.1:46796/v1/hello\n```\n\n## Task endpoint\n\n```toml\n[[artur.endpoints]]\nname = \"echo\"\nmethod = \"POST\"\npath = \"/v1/process/echo/{name}\"\naction = \"task.run\"\ntask = \"echo_json\"\n\n[[artur.tasks]]\nname = \"echo_json\"\nmode = \"sync\"\ncommand = \"python3\"\nargs = [\"examples/scripts/echo.py\", \"--name\", \"{{param.name}}\", \"--source\", \"{{query.source}}\"]\ntimeout_ms = 30000\nstdout_format = \"json\"\n\n[artur.tasks.stdin]\ntype = \"request_json\"\n```\n\nCall it:\n\n```bash\ncurl -sS 'http://127.0.0.1:46796/v1/process/echo/alice?source=demo' \\\n  -H 'content-type: application/json' \\\n  -d '{\"message\":\"hello\"}'\n```\n\nThe task receives a request JSON document on stdin:\n\n```json\n{\n  \"method\": \"POST\",\n  \"uri\": \"/v1/process/echo/alice?source=demo\",\n  \"path\": \"/v1/process/echo/alice\",\n  \"params\": { \"name\": \"alice\" },\n  \"query\": { \"source\": \"demo\" },\n  \"headers\": { \"content-type\": \"application/json\" },\n  \"body\": \"{\\\"message\\\":\\\"hello\\\"}\",\n  \"body_json\": { \"message\": \"hello\" }\n}\n```\n\n## Async task endpoint\n\n```toml\n[[artur.endpoints]]\nname = \"long_task\"\nmethod = \"POST\"\npath = \"/v1/process/long-task\"\naction = \"task.run\"\ntask = \"long_task\"\n\n[[artur.tasks]]\nname = \"long_task\"\nmode = \"async\"\ncommand = \"python3\"\nargs = [\"examples/scripts/long_task.py\"]\ntimeout_ms = 60000\nstdout_format = \"json\"\n\n[artur.tasks.stdin]\ntype = \"body\"\n\n[[artur.endpoints]]\nname = \"get_job\"\nmethod = \"GET\"\npath = \"/v1/jobs/{job_id}\"\naction = \"job.get\"\n```\n\nStarting the task returns a job ID:\n\n```json\n{ \"job_id\": \"4a15d7e2-0f30-45c0-8262-2cad1c939dd0\", \"status\": \"running\" }\n```\n\nThen read it:\n\n```bash\ncurl -sS http://127.0.0.1:46796/v1/jobs/4a15d7e2-0f30-45c0-8262-2cad1c939dd0\n```\n\nCurrent jobs are stored in memory. Use Bria or another durable service when you need durable queues, retries, distributed workers, or long-lived state.\n\n## Template variables\n\nArtur renders templates inside task args, env values, working directories, HTTP step URLs/headers/bodies, and `stdin.type = \"template\"` payloads.\n\n| Template | Value |\n| --- | --- |\n| `{{method}}` | HTTP method. |\n| `{{uri}}` | Full request URI. |\n| `{{path}}` | Request path without query string. |\n| `{{body}}` | Raw request body as text. |\n| `{{request_json}}` or `{{request}}` | Full request context as compact JSON. |\n| `{{param.name}}` | Path parameter, such as `{name}`. |\n| `{{query.name}}` | Query string value. |\n| `{{header.name}}` | Header value, lower-case lookup. |\n| `{{env.NAME}}` | Environment variable from the Artur service process. |\n| `{{body_json.user.id}}` | Field inside a JSON request body. Array indexes are supported, such as `{{body_json.items.0}}`. |\n\nUnknown template keys render as an empty string.\n\n## Task stdin modes\n\n```toml\n[artur.tasks.stdin]\ntype = \"none\"\n```\n\n```toml\n[artur.tasks.stdin]\ntype = \"body\"\n```\n\n```toml\n[artur.tasks.stdin]\ntype = \"request_json\"\n```\n\n```toml\n[artur.tasks.stdin]\ntype = \"template\"\ntemplate = \"user={{body_json.user.id}}\"\n```\n\n\n## Universal shared configuration\n\nArtur now accepts the same universal root sections used by `bria`, `ladon`, `oracles`, and `pano`. Shared profiles live at the root; Artur-owned runtime definitions live under `[artur]`. Other package namespaces are intentionally ignored by Artur so the same `Config.toml` can be mounted into all five services.\n\n```toml\nversion = 1\n\n[log]\nlevel = \"info\"\nformat = \"json\"\n\n[runtime]\nmax_payload_bytes = 1048576\nshutdown_timeout_secs = 30\n\n[http]\nbind = \"127.0.0.1\"\nport = 46796\nprefix = \"v1\"\n\n[stores.artur]\ndriver = \"sqlite\"\nurl = \"sqlite://data/artur/api.sqlite3\"\n\n[stores.ladon]\ndriver = \"sqlite\"\nurl = \"sqlite://data/ladon/addresses.db\"\n\n[stores.pano]\ndriver = \"sqlite\"\nurl = \"sqlite://data/pano/events.db\"\n\n[stores.oracles]\ndriver = \"sqlite\"\nurl = \"sqlite://data/oracles/rates.db\"\n\n[stores.bria]\ndriver = \"sqlite\"\nurl = \"sqlite://data/bria/bria-state.db\"\n\n[ladon]\nstore = \"ladon\"\n\n[pano]\nstore = \"pano\"\n\n[oracles]\nstore = \"oracles\"\n\n[bria.global.state]\nbackend = \"sqlite\"\nstore = \"bria\"\n```\n\nArtur intentionally does not accept root-level `[[endpoints]]`, `[[tasks]]`, or `[server]`. Package-owned configuration must live under `[artur]`, so the same file can also contain `[bria]`, `[ladon]`, `[oracles]`, and `[pano]` without ambiguity.\n\n## Workflows and store operations\n\nA `workflow.run` endpoint executes ready steps in parallel and waits for declared `depends_on` steps before continuing. Each step result is available to later steps through `{{steps.\u003cid\u003e...}}`, so task output can become SQL parameters or another task's input.\n\n```toml\n[[artur.endpoints]]\nname = \"create_space\"\nmethod = \"POST\"\npath = \"/v1/spaces\"\naction = \"workflow.run\"\n\n[artur.endpoints.result]\ninclude_steps = false\nbody = { ok = true, sid = \"{{steps.sid.json.sid}}\", prices_usd = \"{{steps.oracles_prices.json.prices}}\", bria_job = \"{{steps.bria_paid_task.json.job_id}}\" }\n\n[[artur.endpoints.steps]]\nid = \"sid\"\ntype = \"task\"\ntask = \"sid_create\"\n\n[[artur.endpoints.steps]]\nid = \"insert\"\ntype = \"store.execute\"\nstore = \"artur\"\ndepends_on = [\"sid\"]\nsql = \"INSERT INTO spaces (sid, payload) VALUES (?1, ?2)\"\nparams = [\"{{steps.sid.json.sid}}\", \"{{request_json}}\"]\n\n[[artur.endpoints.steps]]\nid = \"lookup\"\ntype = \"store.query\"\nstore = \"artur\"\ndepends_on = [\"insert\"]\nsql = \"SELECT sid, payload FROM spaces WHERE sid = ?1\"\nparams = [\"{{steps.sid.json.sid}}\"]\n```\n\nUse `type = \"http.request\"` when the other package is already running in another container or on another server:\n\n```toml\n[transports.http.ladon]\nbase_url = \"http://ladon:4010/v1\"\ntimeout_ms = 10000\nheaders = { authorization = \"Bearer {{env.LADON_API_KEY}}\" }\n\n[[artur.endpoints.steps]]\nid = \"ladon_addresses\"\ntype = \"http.request\"\ntransport = \"ladon\"\nmethod = \"POST\"\nurl = \"/addresses/checkout\"\ndepends_on = [\"sid\"]\nbody = { sid = \"{{steps.sid.json.sid}}\", chains = [\"evm\", \"solana\", \"btc\"] }\n```\n\nHTTP step output is available as `{{steps.\u003cid\u003e.status}}`, `{{steps.\u003cid\u003e.body}}`, and `{{steps.\u003cid\u003e.json...}}`.\n\nUse `examples/universal-composition.toml` for the full five-package demonstration: create a `sid`, retrieve addresses from `ladon`, track in `pano`, read token and coin USD prices from `oracles`, store the combined record, and launch paid work through `bria`.\n`examples/compose.yaml` shows the same `Config.toml` mounted into `artur`, `ladon`, `pano`, `oracles`, and `bria` containers.\n\n## Ready-to-use paid job service\n\nThe repository also includes `examples/service.toml` and `docker-compose.yml` for an end-to-end service that uses one shared config to create space ULIDs, top up per-chain token balances at a supplied current USD token rate, run immediate or async jobs, and return HTTP 402 x402-native payment requirements when the selected space balance is insufficient.\n\n```bash\ndocker compose up --build\npython3 tests/data_e2e.py\n```\n\nClients can either top up the space first or submit an `x-payment` header for a specific job request.\n\n## Endpoint security\n\nSecurity guards are declared per endpoint. Guards run before the endpoint action and participate in failed-request blocking.\n\n```toml\n[artur.endpoints.security.failure_block]\nkey = \"{{header.authorization}}\"\nmax_failures = 5\nwindow_secs = 300\nblock_secs = 900\n\n[artur.endpoints.security.challenge]\ntask = \"altcha_verify\"\nsuccess_path = \"verified\"\n\n[artur.endpoints.security.x402]\ntask = \"x402_verify\"\nsuccess_path = \"paid\"\n```\n\nSecurity tasks are normal Artur tasks. They should return a successful exit code and JSON with `ok`, `allowed`, `verified`, or `paid` set to `true`, or use `success_path` to name the exact boolean field.\n\n## Challenge and space example\n\n`examples/challenge-space.toml` shows how to model this kind of flow without hardcoding it into Artur:\n\n- `POST /v1/challenge` runs an external `challenge create ...` command.\n- `POST /v1/space` runs your application script, which can call `challenge verify ...`, allocate a random `sid`, assign resources, persist data, and return JSON.\n- `GET /v1/space/{sid}` and `GET /v1/space/` delegate lookup to your application script.\n\nThis 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.\n\n```bash\nexport ARTUR_CHALLENGE_HMAC_SECRET='my-hmac-secret'\nexport ARTUR_CHALLENGE_HMAC_KEY_SECRET='my-key-secret'\nexport ARTUR_SPACE_DB='artur-example-space.sqlite3'\nartur --config examples/challenge-space.toml\n```\n\nIf your preferred challenge crate exposes a binary named `challenge`, this kind of TOML can call it directly:\n\n```toml\n[[artur.tasks]]\nname = \"challenge_create\"\nmode = \"sync\"\ncommand = \"challenge\"\nargs = [\n  \"create\",\n  \"--cost\", \"5000\",\n  \"--random-counter\",\n  \"--expires-in\", \"600\",\n  \"--hmac-secret\", \"{{env.ARTUR_CHALLENGE_HMAC_SECRET}}\",\n  \"--hmac-key-secret\", \"{{env.ARTUR_CHALLENGE_HMAC_KEY_SECRET}}\",\n]\nstdout_format = \"json\"\n```\n\nThe important part is that a developer can swap this for any other implementation by changing TOML, not recompiling Artur.\n\n## Security and operations notes\n\n- Treat the TOML file as privileged code. Anyone who can edit it can define which commands Artur runs.\n- Prefer absolute command paths in production.\n- Keep `timeout_ms` small and specific per task or HTTP step.\n- Avoid placing secrets in command-line args because they may be visible in process listings. Prefer env vars and short-lived credentials.\n- Run Artur as an unprivileged service user.\n- Use a reverse proxy for TLS, authentication, rate limiting, and request logging when exposing Artur outside localhost.\n- Use async task mode for short-lived background work and Bria or another durable queue when results must survive restarts.\n\n## Development\n\nPrerequisites:\n\n- Rust stable matching `rust-version` in `Cargo.toml`.\n- Node.js/npm for the JavaScript and local `npx` e2e coverage.\n- Docker for container build verification.\n\nRun the same core checks as CI:\n\n```bash\ncargo fmt --all -- --check\ncargo clippy --all-targets --all-features -- -D warnings\ncargo test --all-features\ndocker build -t artur:local .\n```\n\nThe e2e test suite starts the compiled `artur` binary and verifies configured static endpoints plus task endpoints backed by JavaScript (`node`), local `npx`, and a compiled Rust helper.\n\n## Docker\n\nThe 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.\n\n```bash\ndocker build -t artur:local .\ndocker run --rm -p 46796:46796 artur:local\n```\n\nGitHub 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.\n\n## License\n\n- MIT license, in `LICENSE`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmelonask%2Fartur","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmelonask%2Fartur","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmelonask%2Fartur/lists"}