|
All checks were successful
Build / build (darwin-amd64, true, x86_64-apple-darwin, true) (push) Successful in 1m46s
Build / build (darwin-arm64, true, aarch64-apple-darwin, true) (push) Successful in 1m48s
Build / build (linux-amd64, false, x86_64-unknown-linux-gnu) (push) Successful in 1m51s
Build / build (linux-arm64, true, aarch64-linux-gnu-gcc, aarch64-unknown-linux-gnu) (push) Successful in 2m1s
|
||
|---|---|---|
| .forgejo/workflows | ||
| docs | ||
| rhai_examples | ||
| scripts | ||
| specs | ||
| src | ||
| .gitignore | ||
| build.sh | ||
| Cargo.lock | ||
| Cargo.toml | ||
| COMPLIANCE_REPORT.md | ||
| install.sh | ||
| instructions_client.md | ||
| instructions_unit_accounting.md | ||
| LICENSE | ||
| README.md | ||
| run.sh | ||
Hero Redis
A high-performance, Redis-compatible server built on redb with ChaCha20-Poly1305 encryption and Ed25519 signature-based authentication.
Features
- Redis Protocol Compatible - Works with any Redis client
- Ed25519 Authentication - Cryptographic signature-based login (no passwords)
- Multi-Database with ACL - Per-database Read/Write/Admin permissions
- Persistent Storage - Data stored in redb (pure Rust embedded database)
- Encryption - All values encrypted with ChaCha20-Poly1305
- Multiple Databases - Up to 1000 databases with lazy loading
- Low Memory - ~3MB at startup, databases loaded on demand
- Auto-cleanup - Idle databases automatically closed after 5 minutes
- Unix Socket & TCP - Both connection methods supported
- Cross-platform - Linux (x86_64, aarch64) and macOS (x86_64, aarch64)
Quick Start
Build from Source
git clone https://github.com/herocode/hero_redis.git
cd hero_redis
cargo build --release
Generate Admin Keypair
# Generate a new Ed25519 keypair
./target/release/hero_redis_login --generate
# Output:
# PUBLIC_KEY=a1b2c3d4... (64 hex chars)
# PRIVATE_KEY=e5f6a7b8... (64 hex chars)
Start Server
# Start with admin public key (required)
./target/release/hero_redis \
--encryption-key "your-encryption-key" \
--admin-pubkey "a1b2c3d4..." \
--data-dir ~/.hero_redis \
--port 6379
Authenticate and Use
# Get a session token
./target/release/hero_redis_login \
--private-key "e5f6a7b8..." \
--port 6379
# Use the token with redis-cli
redis-cli -p 6379
> AUTH <token>
OK
> SET mykey myvalue
OK
Testing
Run the comprehensive test suite to validate the authentication and ACL system:
# Build and run tests (starts server automatically)
cargo run --release --bin hero_redis_tester
The tester:
- Starts a Hero Redis server on port 16379
- Tests CHALLENGE/TOKEN/AUTH authentication flow
- Tests DATABASE.CREATE and user management
- Tests Read/Write/Admin permission enforcement
- Tests database isolation between users
- Cleans up automatically when done
Sample output:
╔════════════════════════════════════════════════════════════════╗
║ Hero Redis Comprehensive Auth & ACL Tester ║
╚════════════════════════════════════════════════════════════════╝
┌────────────────────────────────────────────────────────────────┐
│ Basic Connectivity │
└────────────────────────────────────────────────────────────────┘
✓ PING without auth
✓ GET without auth rejected
┌────────────────────────────────────────────────────────────────┐
│ Admin Authentication Flow │
└────────────────────────────────────────────────────────────────┘
✓ CHALLENGE returns 64 hex chars
✓ TOKEN returns token
✓ AUTH with token
...
╔════════════════════════════════════════════════════════════════╗
║ Test Summary ║
╠════════════════════════════════════════════════════════════════╣
║ Passed: 60 ║
║ Failed: 0 ║
║ Total: 60 ║
╚════════════════════════════════════════════════════════════════╝
🎉 All tests passed!
Authentication Flow
Hero Redis uses Ed25519 signature-based authentication:
Client Server
| |
|-------- CHALLENGE --------------->|
|<------- <random_challenge> -------|
| |
|-- TOKEN <pubkey> <signature> ---->|
|<------- <session_token> ----------|
| |
|-------- AUTH <token> ------------>|
|<------- OK -----------------------|
- CHALLENGE - Request a random challenge (64 hex chars)
- TOKEN - Sign the challenge with your private key, submit with public key
- AUTH - Use the returned session token to authenticate
Command Line Options
Server (hero_redis)
| Option | Default | Description |
|---|---|---|
-d, --data-dir |
~/.hero_redis |
Database directory |
-s, --socket |
~/.hero_redis/redis.sock |
Unix socket path |
-p, --port |
6379 |
TCP port (0 to disable) |
--encryption-key |
required | Encryption key for DB 0 |
--admin-pubkey |
required | Admin public key (can specify multiple) |
-v, --verbose |
false | Enable debug logging |
Login Tool (hero_redis_login)
| Option | Description |
|---|---|
-k, --private-key |
Ed25519 private key (64 hex chars) |
--private-key-file |
Path to private key file |
-p, --port |
Server port (default: 6379) |
-h, --host |
Server host (default: 127.0.0.1) |
-l, --login |
Perform full AUTH after getting token |
-d, --db |
Select database after login |
--generate |
Generate new keypair |
--format |
Output format: token, json, verbose |
Permission Levels
| Level | Can Read | Can Write | Can FLUSHDB | Can USER.GRANT | Can DATABASE.CREATE |
|---|---|---|---|---|---|
| read | ✓ | ✗ | ✗ | ✗ | ✗ |
| write | ✓ | ✓ | ✗ | ✗ | ✗ |
| admin | ✓ | ✓ | ✓ | ✓ (same db) | ✗ |
| server admin | ✓ | ✓ | ✓ | ✓ | ✓ |
Admin Commands
Server Admin Commands (require server admin)
| Command | Description |
|---|---|
ADMIN.ADD <pubkey> |
Add server admin |
ADMIN.REMOVE <pubkey> |
Remove server admin |
ADMIN.LIST |
List server admins |
DATABASE.CREATE <encryption_key> |
Create new database |
DATABASE.STATUS [db|all] |
Show database info |
USER.CREATE <pubkey> |
Register a user |
USER.DELETE <pubkey> |
Delete a user |
Database Admin Commands
| Command | Description |
|---|---|
USER.GRANT <db> <pubkey> <read|write|admin> |
Grant permission |
USER.REVOKE <db> <pubkey> |
Revoke permission |
FLUSHDB |
Clear current database |
Supported Redis Commands
String Commands
GET, SET, MGET, MSET, DEL, EXISTS, EXPIRE, TTL, KEYS, INCR, INCRBY, DECR, DECRBY, APPEND, STRLEN, GETRANGE, SETNX, SETEX, GETSET, TYPE
Hash Commands
HSET, HGET, HMSET, HMGET, HGETALL, HDEL, HEXISTS, HLEN, HKEYS, HVALS, HINCRBY, HSETNX
List Commands
LPUSH, RPUSH, LPOP, RPOP, LRANGE, LLEN, LINDEX, LSET, LREM
Set Commands
SADD, SREM, SMEMBERS, SISMEMBER, SCARD, SPOP, SUNION, SINTER, SDIFF
Stream Commands
XADD, XLEN, XRANGE, XREVRANGE, XREAD, XINFO, XTRIM, XGROUP, XREADGROUP, XACK, XPENDING
Connection Commands
PING, AUTH, SELECT, QUIT, COMMAND
Management Commands
COMPACT, SHUTDOWN, MEMORY USAGE, MEMORY STATS, DBSIZE, FLUSHDB, INFO
Using as a Library
Quick Start
use hero_redis::hero_redis_client::HeroRedisClient;
// Connect with your private key (simplest way)
let mut client = HeroRedisClient::new("127.0.0.1", 6379, "your_private_key_hex")?;
client.select(1)?;
// String operations
client.set("name", "Alice")?;
let name = client.get("name")?.unwrap();
// With expiration (seconds)
client.set_ex("session", "token123", 3600)?;
// Counters
let views = client.incr("page:views")?;
client.incr_by("page:views", 10)?;
// Hashes (like objects/dictionaries)
client.hset("user:1", "name", "Bob")?;
client.hset("user:1", "email", "bob@example.com")?;
let user = client.hgetall("user:1")?;
let email = client.hget("user:1", "email")?;
// Lists (queues)
client.rpush("queue", "job1")?;
client.rpush("queue", "job2")?;
while let Some(job) = client.lpop("queue")? {
println!("Processing: {}", job);
}
// Sets (unique collections)
client.sadd("tags", "rust")?;
client.sadd("tags", "redis")?;
let is_member = client.sismember("tags", "rust")?;
let all_tags = client.smembers("tags")?;
// Batch operations
client.mset(&[("a", "1"), ("b", "2"), ("c", "3")])?;
let values = client.mget(&["a", "b", "c"])?;
// Streams
let id = client.xadd("events", &[("type", "login"), ("user", "alice")])?;
let entries = client.xrange("events", "-", "+")?;
Admin Operations
use hero_redis::hero_redis_client::{HeroRedisClient, Permission};
let mut client = HeroRedisClient::new("127.0.0.1", 6379, "admin_private_key")?;
// Create a new database
let db = client.admin().create_database("db_encryption_key")?;
// Create user and grant permissions
client.admin().create_user(&user_pubkey)?;
client.admin().grant_permission(db, &user_pubkey, Permission::Write)?;
// List admins
let admins = client.admin().list_admins()?;
// Server info
let info = client.admin().info()?;
println!("Version: {:?}", info.version);
Builder Pattern
use hero_redis::hero_redis_client::HeroRedisClient;
let mut client = HeroRedisClient::builder()
.host("127.0.0.1")
.port(6379)
.private_key("your_private_key_hex")?
.database(1) // Auto-select database after connect
.connect()?;
Raw Commands
// Execute any Redis command
let result: String = client.cmd(&["INFO", "server"])?;
let count: i64 = client.cmd_int(&["DBSIZE"])?;
let keys: Vec<String> = client.cmd_vec(&["KEYS", "*"])?;
Architecture
hero_redis/
├── src/
│ ├── main.rs # Server CLI entry point
│ ├── lib.rs # Library exports
│ ├── hero_redis_server/
│ │ ├── mod.rs # Module exports
│ │ ├── server.rs # TCP/Unix server + auth
│ │ ├── auth.rs # Ed25519 authentication
│ │ ├── backend.rs # redb storage engine
│ │ ├── encrypted_backend.rs # Encryption wrapper
│ │ ├── crypto.rs # ChaCha20-Poly1305
│ │ └── protocol.rs # RESP v2 parser
│ ├── hero_redis_client/
│ │ ├── mod.rs # Client module exports
│ │ ├── client.rs # Main client implementation
│ │ ├── admin.rs # Admin operations
│ │ ├── builder.rs # Builder pattern
│ │ ├── commands.rs # Command options & types
│ │ └── error.rs # Error types
│ ├── hero_redis_login/
│ │ └── mod.rs # Low-level login client
│ ├── bin/
│ │ └── hero_login.rs # Login CLI (hero_redis_login)
│ └── hero_redis_tester/
│ └── main.rs # Comprehensive test suite
├── specs/
│ ├── hero_redis_server_multi_db.md # Multi-DB auth spec
│ └── hero_login.md # Login protocol spec
└── docs/
└── *.md # Documentation
Data Storage
- Data directory:
~/.hero_redis/(configurable) - Database files:
db0.redb,db1.redb, ...db999.redb - DB 0 is reserved for admin metadata (admin registry, user permissions)
- Each database has its own encryption key
- Values are encrypted with ChaCha20-Poly1305 before storage
License
Apache 2.0