Rack + Puma web cache server for Mudis. This app runs Mudis in IPC mode inside the Puma master process and exposes HTTP endpoints for cache interaction. It bundles mudis-cli and mudis-ql by default.
- Ruby >= 3.3
- Docker (optional)
cd mudis-web-cache
docker compose -f docker-compose.yml up --buildApp will listen on http://localhost:3000.
By default, the compose file enables Mudis soft persistence and writes snapshots to ./data/mudis_snapshot.dump
(mounted into the container at /data).
cd mudis-web-cache
bundle install
bundle exec puma -C config/puma.rbAll responses are JSON.
GET /healthGET /metricsPOST /metrics/resetPOST /resetGET /cache/:key?namespace=...POST /cache/:key?namespace=...PUT /cache/:key?namespace=...DELETE /cache/:key?namespace=...GET /cache/:key/inspect?namespace=...GET /exists/:key?namespace=...GET /keys?namespace=...DELETE /namespace/:namespaceGET /least-touched?count=10POST /qlGET /docsGET /openapi.json
JWT authentication is enabled by default. Set MUDIS_JWT_ENABLED=false to opt out.
Requests must include a bearer token:
curl -H "Authorization: Bearer <token>" "http://localhost:3000/cache/user:42?namespace=users"Admin endpoints require an admin token (separate from read/write tokens). Admin endpoints:
POST /resetPOST /metrics/resetDELETE /namespace/:namespace
The default admin check looks for a role claim set to admin. You can change the claim name/value with env vars.
Example admin claim payload:
{"sub":"service","role":"admin"}This service does not issue tokens. Callers must mint their own JWTs (signed with MUDIS_JWT_SECRET and using MUDIS_JWT_ALGORITHM) and include them as a bearer token on each request.
If you are not using Docker, you still need to set MUDIS_JWT_SECRET before starting the server. For local development and testing, prefer a deterministic secret so your tokens stay valid across restarts:
# Bash
export MUDIS_JWT_SECRET="dev-secret"# PowerShell
$env:MUDIS_JWT_SECRET = "dev-secret"# Ruby
ruby -e "puts 'dev-secret'"You can disable JWT locally by setting MUDIS_JWT_ENABLED=false before starting the server.
# Bash
export MUDIS_JWT_ENABLED="false"
bundle exec puma -C config/puma.rb# PowerShell
$env:MUDIS_JWT_ENABLED = "false"
bundle exec puma -C config/puma.rb# Ruby
ruby -e "ENV['MUDIS_JWT_ENABLED']='false'; exec('bundle exec puma -C config/puma.rb')"You can opt in to per-caller isolation by binding a namespace to a JWT claim. When enabled, the service derives the namespace from the claim and ignores client-supplied namespaces (rejecting mismatches).
Example: with MUDIS_BIND_NAMESPACE_CLAIM=sub and MUDIS_BIND_PREFIX=caller:, a token with {"sub":"acme"} is bound to namespace caller:acme.
curl -X POST "http://localhost:3000/cache/user:42?namespace=users" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"value": {"name": "Ada", "age": 38}, "expires_in": 60}'curl -H "Authorization: Bearer <token>" "http://localhost:3000/cache/user:42?namespace=users"curl -X POST "http://localhost:3000/ql" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{
"namespace": "users",
"where": {"age": {"range": [30, 50]}},
"order": {"field": "age", "direction": "asc"},
"limit": 10,
"action": "all"
}'Supported QL payload fields:
namespacestring or nullwherehash of field to conditionorderas{ "field": "age", "direction": "asc" }or a field stringlimitintegeroffsetintegeractionone ofall,first,last,count,exists,pluck,sum,average,group_byfieldsarray forpluckfieldstring forsum,average,group_by
Supported where conditions:
- Equality:
{ "status": "active" } - Regex:
{ "name": {"regex": "^A", "flags": "i"} } - Range:
{ "age": {"range": [18, 30]} } - In list:
{ "role": {"in": ["admin", "editor"]} }
Open http://localhost:3000/docs for interactive API docs. The OpenAPI spec is available at http://localhost:3000/openapi.json.
The image includes mudis-cli. This wrapper is intended for IPC usage only and connects to the running cache via IPC. Use it for runtime interaction:
docker exec -it <container> ./bin/mudis keys --namespace usersThe CLI process is a separate client that connects to the cache over IPC. If IPC mode is disabled, the CLI will exit with an error.
PORT(default3000)RACK_ENV(defaultproduction)WEB_CONCURRENCY(default2)PUMA_THREADS(default5)MUDIS_SERIALIZER(json,marshal,oj)MUDIS_JWT_ENABLED(true/false, defaulttrue)MUDIS_JWT_SECRET(required when JWT is enabled)MUDIS_JWT_ALGORITHM(defaultHS256)MUDIS_JWT_ISSUER(optional)MUDIS_JWT_AUDIENCE(optional)MUDIS_JWT_ADMIN_CLAIM(defaultrole)MUDIS_JWT_ADMIN_VALUE(defaultadmin)MUDIS_BIND_ENABLED(true/false, defaultfalse)MUDIS_BIND_NAMESPACE_CLAIM(defaultsub)MUDIS_BIND_PREFIX(default empty)MUDIS_COMPRESS(true/false)MUDIS_MAX_VALUE_BYTESMUDIS_HARD_MEMORY_LIMIT(true/false)MUDIS_MAX_BYTESMUDIS_BUCKETSMUDIS_MAX_TTLMUDIS_DEFAULT_TTLMUDIS_EXPIRY_INTERVAL(seconds, default60)MUDIS_PERSISTENCE_ENABLED(true/false)MUDIS_PERSISTENCE_PATHMUDIS_PERSISTENCE_FORMAT(jsonormarshal)MUDIS_PERSISTENCE_SAFE_WRITE(true/false)MUDIS_FORCE_TCP(true/false) for IPC over TCP (Windows or dev)MUDIS_IPC_MODE(true/false, defaulttrue)MUDIS_SSL_ENABLED(true/false)MUDIS_SSL_PORT(defaults toPORT)MUDIS_SSL_CERT(path to cert PEM)MUDIS_SSL_KEY(path to key PEM)MUDIS_SSL_VERIFY_MODE(defaultnone)MUDIS_SSL_CA(path to CA bundle PEM)MUDIS_SSL_MIN_VERSIONMUDIS_SSL_MAX_VERSION
Notes:
- IPC mode is enabled via Puma preload + IPC server in
config/puma.rbwhenMUDIS_IPC_MODE=trueand workers > 0. - When
MUDIS_PERSISTENCE_ENABLED=true, the server loads the snapshot on startup and saves it on exit (via the Mudis persistence hook). MUDIS_PERSISTENCE_PATHcontrols where the snapshot is written (only when persistence is enabled). In containers, set this to a mounted path or shared volume.- On Windows, Puma workers are disabled automatically (worker mode not supported).
- For
MUDIS_SERIALIZER=oj, you must add theojgem (not included by default). - SSL is optional. When enabled, you must supply
MUDIS_SSL_CERTandMUDIS_SSL_KEY.