Getting Started
Dunena is a high-performance in-memory cache engine with a Zig core and a TypeScript/Bun API layer. This guide walks you through installation and basic usage.
Prerequisites
Choose Your Install Method
| Method | Best For | Prerequisites |
|---|---|---|
| 🐳 Docker | Quick trial, deployment | Docker only |
| 📦 GitHub Release | Standalone server (Linux) | Bun only |
| 🔧 Source Build | Development, all platforms | Bun + Zig |
See the full INSTALL.md for detailed instructions per method.
Docker Quickstart (No Zig/Bun needed)
# From the repo root:
docker compose -f deploy/docker-compose.yml up -d
# Verify it's running
curl http://localhost:3000/health
# Try it
curl -X POST http://localhost:3000/cache/hello \
-H "Content-Type: application/json" \
-d '{"value": "world"}'
curl http://localhost:3000/cache/hello
Source Build Prerequisites
Installation (Source Build)
# Clone the repository
git clone https://github.com/owenbellowen/dunena.git
cd dunena
# Install TypeScript dependencies
bun install
# Build the native Zig cache library
bun run build:zig
The Zig build compiles the native cache, bloom filter, compression, and statistics modules into a shared library that Bun calls via FFI.
Verify Your Setup
# Run the sanity checker
bun run cli -- doctor
Quick Start
# Start the server
bun run start
Run the CLI from monorepo root
bun run cli -- stats
bun run cli -- get greeting
By default, Dunena starts on http://localhost:3000 with:
- WebSocket at
ws://localhost:3000/ws - Dashboard at
http://localhost:3000/dashboard - Documentation at
http://localhost:3000/docs
Your First Requests
Store a value
curl -X POST http://localhost:3000/cache/greeting \
-H "Content-Type: application/json" \
-d '{"value": "Hello, Dunena!"}'
{"ok": true}
Read it back
curl http://localhost:3000/cache/greeting
{"key": "greeting", "value": "Hello, Dunena!"}
Delete it
curl -X DELETE http://localhost:3000/cache/greeting
{"deleted": true}
Batch operations
# Store multiple keys
curl -X POST http://localhost:3000/cache \
-H "Content-Type: application/json" \
-d '{
"action": "mset",
"entries": [
{"key": "user:1", "value": "Alice"},
{"key": "user:2", "value": "Bob"},
{"key": "user:3", "value": "Charlie"}
]
}'
# Fetch multiple keys
curl -X POST http://localhost:3000/cache \
-H "Content-Type: application/json" \
-d '{"action": "mget", "keys": ["user:1", "user:2", "user:99"]}'
{"result": {"user:1": "Alice", "user:2": "Bob", "user:99": null}}
Namespaces
Namespaces let you isolate groups of keys. Keys in one namespace are completely invisible to other namespaces.
# Store in namespace "sessions"
curl -X POST "http://localhost:3000/cache/token-abc?ns=sessions" \
-H "Content-Type: application/json" \
-d '{"value": "user-42"}'
# Read from the same namespace
curl "http://localhost:3000/cache/token-abc?ns=sessions"
# → {"key": "token-abc", "value": "user-42"}
# Without namespace — not found
curl "http://localhost:3000/cache/token-abc"
# → 404 {"error": "Key not found"}
TTL (Time-To-Live)
Set a TTL in milliseconds to automatically expire keys.
# Expire after 30 seconds
curl -X POST http://localhost:3000/cache/temp-data \
-H "Content-Type: application/json" \
-d '{"value": "short-lived", "ttl": 30000}'
After 30 seconds the key is automatically deleted and a expired event is published to WebSocket subscribers.
You can also set a global default TTL via the DUNENA_DEFAULT_TTL environment variable. Per-key TTL always overrides the default.
Scanning Keys
# List all keys
curl "http://localhost:3000/keys"
# Filter by pattern
curl "http://localhost:3000/keys?pattern=user-*"
# Filter by namespace
curl "http://localhost:3000/keys?pattern=*&ns=sessions"
Next Steps
- API Reference — Full endpoint documentation
- WebSocket — Real-time cache operations
- CLI Tool — Command-line client
- Configuration — All environment variables