A lightning-fast, tag-aware in-memory cache server written in Rust
Built for speed, designed for simplicity, ready for production
TagCache is a high-performance, sharded, tag-aware in-memory cache server that offers:
🔹 JSON HTTP API (port 8888) - RESTful interface for web applications 🔹 TCP Protocol (port 1984) - Ultra-low latency binary protocol 🔹 Tag-based Invalidation - Organize and clear related data efficiently 🔹 Atomic Operations - ADD, INCR, DECR with race-condition protection 🔹 Built-in Web Dashboard - Beautiful React UI for monitoring and management 🔹 CLI Interface - Complete command-line control 🔹 Production Ready - Authentication, monitoring, and deployment tools
| Feature | Description |
|---|---|
| 🏎️ Blazing Fast | Multi-shard design with DashMap + hash sharding for maximum concurrency |
| 🏷️ Tag-Aware | Associate multiple tags with keys for advanced invalidation strategies |
| ⚛️ Atomic Operations | ADD, INCR, DECR with race-condition protection like Redis |
| ⏱️ Flexible TTL | Precise expiration control in milliseconds or seconds |
| 🔄 Dual Protocols | Choose between JSON HTTP API or high-performance TCP protocol |
| 📊 Rich Monitoring | Real-time statistics, performance metrics, and health checks |
| 🔐 Secure by Default | Built-in authentication with password management |
| 🌍 Cross-Platform | Native support for macOS, Linux, and Windows |
| 📦 Easy Deploy | Homebrew, Debian packages, Docker, and binary releases |
- 🚀 Quick Start
- 📥 Installation
- ⚙️ Configuration Management
- 🔐 Authentication & Security
- 💻 Command Line Interface
- 🌐 HTTP JSON API
- ⚡ TCP Protocol
- 📊 Performance Testing
- 🐳 Docker
- ⚙️ Configuration
- 🔧 Development
# Install (choose your method)
brew install aminshamim/tap/tagcache
# Start server
tagcache server
# Use CLI with default credentials (admin/password)
tagcache --username admin --password password put "hello" "world"
tagcache --username admin --password password add "counter" "100"
tagcache --username admin --password password increment "page_views" --by 1
tagcache --username admin --password password decrement "quota" --by 5
tagcache --username admin --password password get key "hello"
tagcache --username admin --password password stats
# Visit web dashboard
open http://localhost:8888Download the latest binaries from our GitHub Releases page.
# For macOS Intel (x86_64)
curl -L -o tagcache-macos-x86_64.tar.gz \
https://github.com/aminshamim/tagcache/releases/download/v1.0.8/tagcache-macos-x86_64.tar.gz
tar xzf tagcache-macos-x86_64.tar.gz
sudo cp tagcache bench_tcp /usr/local/bin/
# For macOS Apple Silicon (ARM64/M1/M2/M3)
curl -L -o tagcache-macos-arm64.tar.gz \
https://github.com/aminshamim/tagcache/releases/download/v1.0.8/tagcache-macos-arm64.tar.gz
tar xzf tagcache-macos-arm64.tar.gz
sudo cp tagcache bench_tcp /usr/local/bin/
# Verify installation
tagcache --version# For Linux x86_64
curl -L -o tagcache-linux-x86_64.tar.gz \
https://github.com/aminshamim/tagcache/releases/download/v1.0.8/tagcache-linux-x86_64.tar.gz
tar xzf tagcache-linux-x86_64.tar.gz
sudo cp tagcache bench_tcp /usr/local/bin/
# For Linux ARM64 (Raspberry Pi, ARM servers)
curl -L -o tagcache-linux-arm64.tar.gz \
https://github.com/aminshamim/tagcache/releases/download/v1.0.8/tagcache-linux-arm64.tar.gz
tar xzf tagcache-linux-arm64.tar.gz
sudo cp tagcache bench_tcp /usr/local/bin/
# Verify installation
tagcache --version# Download and extract
curl -L -o tagcache-windows-x86_64.zip \
https://github.com/aminshamim/tagcache/releases/download/v1.0.8/tagcache-windows-x86_64.zip
# Extract tagcache.exe and bench_tcp.exe to your preferred location
# Add to PATH or run directly: .\tagcache.exe --version# Add our tap and install
brew tap aminshamim/tap
brew install tagcache
# Or install directly
brew install aminshamim/tap/tagcache
# Verify installation
tagcache --version# Download the latest .deb package
curl -L -o tagcache.deb \
https://github.com/aminshamim/tagcache/releases/download/v1.0.8/tagcache_1.0.8_amd64.deb
# Install the package
sudo dpkg -i tagcache.deb
# Or if you prefer to auto-resolve dependencies
sudo apt install ./tagcache.deb
# Start and enable the service
sudo systemctl enable tagcache
sudo systemctl start tagcache
# Check status
sudo systemctl status tagcache# Download the latest .rpm package
curl -L -o tagcache.rpm \
https://github.com/aminshamim/tagcache/releases/download/v1.0.8/tagcache-1.0.8-1.x86_64.rpm
# Install the package
sudo rpm -ivh tagcache.rpm
# Or using dnf/yum
sudo dnf install ./tagcache.rpm
# Start and enable the service
sudo systemctl enable tagcache
sudo systemctl start tagcache
# Check status
sudo systemctl status tagcache# Run with default ports
docker run -d --name tagcache \
-p 8888:8888 -p 1984:1984 \
ghcr.io/aminshamim/tagcache:latest
# Or with custom configuration
docker run -d --name tagcache \
-p 9090:8888 -p 1985:1984 \
-e NUM_SHARDS=32 \
-e CLEANUP_INTERVAL_MS=5000 \
ghcr.io/aminshamim/tagcache:latest
# Check logs
docker logs tagcache# Install from Git (latest development version)
cargo install --git https://github.com/aminshamim/tagcache --features embed-ui
# Or clone and build locally
git clone https://github.com/aminshamim/tagcache.git
cd tagcache
./scripts/build-and-release.shEach release includes binaries for all major platforms:
| Platform | Architecture | Download |
|---|---|---|
| macOS | Intel (x86_64) | tagcache-macos-x86_64.tar.gz |
| macOS | Apple Silicon (ARM64) | tagcache-macos-arm64.tar.gz |
| Linux | x86_64 | tagcache-linux-x86_64.tar.gz |
| Linux | ARM64 | tagcache-linux-arm64.tar.gz |
| Linux | x86_64 (musl) | tagcache-linux-x86_64-musl.tar.gz |
| Linux | ARM64 (musl) | tagcache-linux-arm64-musl.tar.gz |
| Windows | x86_64 | tagcache-windows-x86_64.zip |
| Debian | x86_64 | tagcache_X.X.X_amd64.deb |
| RPM | x86_64 | tagcache-X.X.X-1.x86_64.rpm |
After installation, verify TagCache is working:
# Check version
tagcache --version
# Verify bench_tcp is also available
bench_tcp --help
# Start server (Ctrl+C to stop)
tagcache server
# In another terminal, test basic operations
tagcache --username admin --password password put "test" "hello world"
tagcache --username admin --password password get key "test"
tagcache --username admin --password password stats
# Test performance with bench_tcp
bench_tcp localhost 1984 32 5 # host port connections duration_seconds
# Open web dashboard
open http://localhost:8888 # macOS
# or visit http://localhost:8888 in your browserAll TagCache distributions include both binaries:
tagcache- Main cache server with CLI interfacebench_tcp- High-performance TCP protocol benchmark tool
Both tools are available in:
- ✅ All binary releases (.tar.gz, .zip)
- ✅ Debian packages (.deb) - installed to
/usr/bin/ - ✅ RPM packages (.rpm) - installed to
/usr/bin/ - ✅ Homebrew formula
- ✅ Docker images
cargo build --release
./target/release/tagcacheServer starts HTTP on :8888 and TCP on :1984 by default.
Primary (preferred):
PORT– HTTP port (default 8888)TCP_PORT– TCP protocol port (default 1984)NUM_SHARDS– number of shards (default 16)CLEANUP_INTERVAL_MS– sweep interval in ms (fallback to seconds if not set)CLEANUP_INTERVAL_SECONDS– sweep interval in seconds (if ms not set)
Legacy (still accepted, logged when used):
TC_HTTP_PORT,TC_TCP_PORT,TC_NUM_SHARDS,TC_SWEEP_INTERVAL_MS
TagCache uses a configuration file system similar to php.ini or nginx.conf for persistent settings. All configuration changes persist across server restarts.
TagCache automatically looks for tagcache.conf in:
- Current working directory (highest priority)
- User config directory (
~/.config/tagcache/tagcache.confon Linux/macOS)
# Check current configuration file path
tagcache config path
# Show current configuration
tagcache config show# Create configuration file with defaults (if it doesn't exist)
tagcache config show # Creates tagcache.conf with defaults
# Change authentication credentials
tagcache config set authentication.username "my_admin"
tagcache config set authentication.password "secure_password_123"
# Change server settings
tagcache config set server.http_port 9090
tagcache config set server.tcp_port 1985
tagcache config set server.num_shards 32
# Change cache settings
tagcache config set cache.max_key_length 2048
tagcache config set logging.level "debug"
# Reset to defaults
tagcache config reset
# View example configuration
cat tagcache.conf.exampleThe configuration file includes these sections:
[server]- HTTP/TCP ports, shards, cleanup interval[authentication]- Username, password, token lifetime[cache]- TTL settings, size limits, tag limits[logging]- Log level, format, file output[performance]- TCP settings, connection limits[security]- Auth requirements, rate limiting, IP restrictions
Configuration changes via CLI persist to the file immediately:
# Change password via configuration
tagcache config set authentication.password "new_password"
# Change password via API (also persists to config file)
tagcache --username admin --password current_password change-password "new_password"
# Both methods update tagcache.conf and persist across server restartsImportant: Server restart is required for configuration changes to take effect.
TagCache includes built-in authentication with default credentials and flexible management options.
TagCache starts with secure default credentials:
- Username:
admin - Password:
password
# Change the password (requires current credentials)
tagcache --username admin --password password change-password "your-new-secure-password"# Master reset to default credentials (requires current credentials)
tagcache --username admin --password current-password reset-credentials
# After reset: username=admin, password=password# 1. Change from defaults
tagcache --username admin --password password change-password "MySecure123!"
# 2. Use new password for operations
tagcache --username admin --password "MySecure123!" stats
# 3. Reset if needed (emergency recovery)
tagcache --username admin --password "MySecure123!" reset-credentialsAll API endpoints (except /health) require authentication using Basic Auth:
# Using current credentials
curl -u admin:password http://localhost:8888/stats
# Or with explicit Basic Auth header
echo -n "admin:password" | base64 # YWRtaW46cGFzc3dvcmQ=
curl -H "Authorization: Basic YWRtaW46cGFzc3dvcmQ=" http://localhost:8888/stats# Get an auth token (alternative to Basic Auth)
curl -s -X POST http://localhost:8888/auth/login \
-u admin:password \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"password"}'Response:
{"token":"<48-char-random>","expires_in":3600}# Use token instead of username/password
TOKEN="your-token-here"
curl -H "Authorization: Bearer $TOKEN" http://localhost:8888/stats# Rotate to new random credentials (invalidates all tokens)
curl -X POST http://localhost:8888/auth/rotate \
-H "Authorization: Bearer $TOKEN"Response:
{"ok":true,"username":"newRandomUser","password":"newRandomPass"}- Change Default Password: Always change from
admin/passwordin production - Use HTTPS: Put TagCache behind a TLS proxy (nginx, Caddy, Traefik)
- Network Security: Bind to specific interfaces, use firewalls
- Environment Variables: Set
ALLOWED_ORIGINfor CORS restrictions - Regular Rotation: Use the CLI or API to rotate credentials periodically
- Default:
admin/password(change immediately!) - CLI Management:
change-passwordandreset-credentialscommands - API Access: Basic Auth for all endpoints (except health check)
- Web Dashboard: Integrated login with token management
- Token-based: Optional Bearer token authentication
- Emergency Recovery: Master reset command available
Base URL: http://host:PORT
TagCache includes a comprehensive CLI for all cache operations. Perfect for scripts, testing, and interactive use.
# Start server
tagcache server
# Basic operations (use your credentials from credential.txt)
tagcache --username <user> --password <pass> put mykey "my value" --tags "tag1,tag2"
tagcache --username <user> --password <pass> get key mykey
tagcache --username <user> --password <pass> statstagcache put <key> <value>- Store data with optional tags and TTLtagcache add <key> <value>- Atomically add data (fails if key exists)tagcache increment <key>- Atomically increment numeric value (creates if not exists)tagcache decrement <key>- Atomically decrement numeric value (creates if not exists)tagcache get key <key>- Retrieve value by keytagcache get tag <tags>- Get keys by comma-separated tagstagcache flush key <key>- Remove specific keytagcache flush tag <tags>- Remove all keys with tagstagcache flush all- Clear entire cache
tagcache stats- Show detailed statisticstagcache status- Show server statustagcache health- Health check (no auth required)tagcache restart- Restart instructions
tagcache change-password <new-password>- Change the passwordtagcache reset-credentials- Reset to default admin/password
tagcache server- Start the TagCache server
# Store session data with 1-hour TTL
tagcache put "session:abc123" "user_data" --tags "session,user:1001" --ttl-ms 3600000
# Atomically add new user counter (fails if exists)
tagcache add "user:new" "100" --tags "counter,user"
# Increment a counter (creates with value 1 if doesn't exist)
tagcache increment "page_views" --by 1 --tags "metrics,counter"
# Increment by custom amount
tagcache increment "user:points:123" --by 50 --tags "points,user:123"
# Decrement with TTL (useful for rate limiting)
tagcache decrement "rate_limit:api:user123" --by 1 --ttl-ms 60000 --tags "rate_limit"
# Get all active sessions
tagcache get tag "session,active"
# Remove expired sessions
tagcache flush tag "session,expired"
# Check cache performance
tagcache statsThe HTTP interface is JSON-based and ideal for web applications.
For all examples below, first export a Basic Auth header (reads bootstrap credentials from credential.txt):
USER=$(grep '^username=' credential.txt | cut -d= -f2)
PASS=$(grep '^password=' credential.txt | cut -d= -f2)
B64=$(printf '%s:%s' "$USER" "$PASS" | base64)
AUTH="-H Authorization: Basic $B64"Then prepend $AUTH (or copy the header literal) to every curl command.
Store/update a value.
curl -X POST http://127.0.0.1:8888/put \
-H "Authorization: Basic $B64" \
-H 'Content-Type: application/json' \
-d '{"key":"user:42","value":"hello","tags":["users","trial"],"ttl_ms":6000000}'Response:
{"ok":true,"ttl_ms":60000}Atomically add a value (fails if key already exists).
curl -X POST http://127.0.0.1:8888/add \
-H "Authorization: Basic $B64" \
-H 'Content-Type: application/json' \
-d '{"key":"counter:new","value":"100","tags":["counters"],"ttl_ms":3600000}'Response (success):
{"ok":true,"added":true,"ttl_ms":3600000}Response (key exists):
{"ok":true,"added":false,"ttl_ms":null}Atomically increment a numeric value. Creates key with increment amount if it doesn't exist.
curl -X POST http://127.0.0.1:8888/incr \
-H "Authorization: Basic $B64" \
-H 'Content-Type: application/json' \
-d '{"key":"page_views","by":1,"tags":["metrics"],"ttl_ms":86400000}'Response (success):
{"ok":true,"value":42,"ttl_ms":86400000}Response (error - not numeric):
{"ok":false,"error":"value is not an integer"}Atomically decrement a numeric value. Creates key with negative decrement amount if it doesn't exist.
curl -X POST http://127.0.0.1:8888/decr \
-H "Authorization: Basic $B64" \
-H 'Content-Type: application/json' \
-d '{"key":"remaining_quota","by":5,"tags":["quotas","user:123"],"ttl_ms":3600000}'Response (success):
{"ok":true,"value":95,"ttl_ms":3600000}Response (error):
{"ok":false,"error":"integer overflow"}curl -H "Authorization: Basic $B64" http://127.0.0.1:8888/get/user:42Response (hit):
{"value":"hello"}Response (miss):
{"error":"not_found"}curl -H "Authorization: Basic $B64" 'http://127.0.0.1:8888/keys-by-tag?tag=users&limit=50'Response:
{"keys":["user:42", "user:7"]}curl -X POST http://127.0.0.1:8888/invalidate-key \
-H "Authorization: Basic $B64" \
-H 'Content-Type: application/json' \
-d '{"key":"user:42"}'Response: { "success": true }
curl -X POST http://127.0.0.1:8888/invalidate-tag \
-H "Authorization: Basic $B64" \
-H 'Content-Type: application/json' \
-d '{"tag":"trial"}'Response: { "success": true, "count": <removed> }
curl -H "Authorization: Basic $B64" http://127.0.0.1:8888/statsResponse (extended fields may appear in newer versions):
{
"hits": 10,
"misses": 2,
"puts": 12,
"invalidations": 1,
"hit_ratio": 0.8333,
"items": 2500,
"bytes": 1827364,
"tags": 37
}Line-based, tab-delimited. One command per line. Fields separated by \t (TAB). Newline terminates command.
Commands:
PUT <key> <ttl_ms|- > <tag1,tag2|- > <value>
ADD <key> <ttl_ms|- > <tag1,tag2|- > <value>
INCR <key> [by] [ttl_ms|-] [tag1,tag2|-]
DECR <key> [by] [ttl_ms|-] [tag1,tag2|-]
GET <key>
DEL <key>
INV_TAG <tag>
KEYS_BY_TAG <tag> (alias: KEYS <tag>)
STATS
Responses (one line):
OK | ERR <msg>
ADDED | EXISTS
VALUE <value> | NF | ERR <error>
DEL ok | DEL nf
INV_TAG <count>
KEYS <k1,k2,...>
STATS <hits> <misses> <puts> <invalidations> <hit_ratio>
Example session (using nc and showing literal tabs as ↹ for clarity):
PUT↹user:1↹60000↹users,trial↹hello world
OK
ADD↹counter:new↹-↹counters↹100
ADDED
ADD↹counter:new↹-↹-↹200
EXISTS
INCR↹page_views↹1↹86400000↹metrics
VALUE 1
INCR↹page_views↹5
VALUE 6
DECR↹user_quota↹10↹-↹quotas
VALUE -10
GET↹user:1
VALUE hello world
INV_TAG↹trial
INV_TAG 1
GET↹user:1
NF
- PUT: Store/update a value (upsert)
- ADD: Atomically add only if key doesn't exist (returns ADDED/EXISTS)
- INCR: Atomically increment numeric value (by=1 if omitted, creates if not exists)
- DECR: Atomically decrement numeric value (by=1 if omitted, creates if not exists)
- GET: Retrieve value (returns VALUE or NF for not found)
- DEL: Delete key (returns DEL ok/nf)
- INV_TAG: Invalidate all keys with tag (returns count)
- KEYS: List keys by tag
- STATS: Server statistics Notes:
- Value, key, and tags must not contain tabs or newlines.
-means no TTL or no tags.- No escaping layer; for binary or large payloads consider a future binary protocol.
Build image:
docker build -t tagcache:latest .Run:
docker run --rm -p 8888:8888 -p 1984:1984 tagcache:latestEnvironment overrides:
docker run -e NUM_SHARDS=64 -e CLEANUP_INTERVAL_MS=5000 -p 8888:8888 -p 1984:1984 tagcache:latestProvided under examples/php/test.php:
php examples/php/test.phpSet TAGCACHE_URL for a custom base URL.
TagCache includes bench_tcp, a high-performance benchmarking tool that's available in all distributions:
# Basic benchmark (default: localhost:1984, 32 connections, 10 seconds)
bench_tcp
# Custom benchmark parameters
bench_tcp <host> <port> <connections> <duration_seconds>
bench_tcp localhost 1984 64 15
# Benchmark different modes
bench_tcp --mode put localhost 1984 32 10 # Test PUT operations
bench_tcp --mode get localhost 1984 32 10 # Test GET operations (default)
# Custom TTL and key count
bench_tcp --ttl 30000 --keys 1000 localhost 1984 32 5Benchmark config: host=127.0.0.1 port=1984 conns=32 duration=10s keys=100 mode=get ttl_ms=60000
Results:
Total ops: 1877859
Throughput: 187785.90 ops/sec
Latency (microseconds): min 19.4 p50 166.2 p90 224.4 p95 244.6 p99 289.9 max 914.4 avg 170.2
--mode- Operation type:get(default) orput--ttl- TTL in milliseconds (default: 60000)--keys- Number of unique keys to cycle through (default: 100)
The benchmark tool tests the high-performance TCP protocol and can achieve very high throughput rates depending on your hardware.
Installation Verification: After installing TagCache from any source, bench_tcp should be immediately available:
# Verify installation
which bench_tcp
bench_tcp --helpput.lua:
wrk.method = "POST"
wrk.path = "/put"
wrk.headers["Content-Type"] = "application/json"
function request()
local k = math.random(1,100000)
return wrk.format(nil, nil, nil, '{"key":"k'..k..'","value":"v","tags":["t"],"ttl_ms":60000}')
endRun:
wrk -t8 -c64 -d10s -s put.lua http://127.0.0.1:8888get.lua:
math.randomseed(os.time())
function request()
local k = math.random(1,100000)
return wrk.format("GET", "/get/k"..k)
endRun:
wrk -t8 -c64 -d10s -s get.lua http://127.0.0.1:8888- Increase
NUM_SHARDSto reduce contention (power of 2 often helps hash distribution) - Use
RUSTFLAGS="-C target-cpu=native"for maximum local CPU optimizations - Pin process / adjust OS networking (e.g.
ulimit -n, TCP backlog) for very high concurrency - Reduce JSON overhead by preferring TCP protocol in latency-sensitive paths
- No persistence (in-memory only)
- No replication / clustering (future: consistent hashing + peer discovery)
- No compression / binary protocol (planned binary frame optional layer)
- Tag cardinality not bounded (monitor memory usage with many distinct tags)
Run in debug:
cargo runRun benchmarks (release recommended):
cargo build --release
./target/release/bench_tcp --mode get --conns 32 --duration 5 --keys 5000MIT License - see LICENSE file for details.
Md. Aminul Islam Sarker
- 📧 Email: [email protected]
- 💼 LinkedIn: https://www.linkedin.com/in/aminshamim/
- 🐙 GitHub: @aminshamim
PRs and issues welcome!