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

MethodBest ForPrerequisites
🐳 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:

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