Architecture
Tellstone is organized into a small set of packages, each with one job:
| Layer | Package | Notes |
|---|---|---|
| Binary protocol | client (public), internal/network (impl) | MsgRequest/MsgResponse frames (GET/SET/DEL, TTL, key, value) |
| RESP2 protocol | internal/resp | Redis-compatible listener reusing the same engine; optional STARTTLS upgrade |
| Storage engine | internal/storage | GOMAXPROCS sharded buckets (--num-shards), per-shard RWMutex, timing-wheel eviction |
| Persistence | internal/persistence | Per-shard append-only WAL for crash recovery (--enable-persistence) |
| Crypto | internal/crypto | Optional ChaCha20-Poly1305 for at-rest encryption |
| Transport security | internal/tls | Optional TLS 1.3 / mTLS with automatic certificate rotation |
| RBAC | internal/rbac | Optional per-user auth + role-based command gating; atomic policy snapshots |
| Metrics / tracing | internal/metrics, internal/trace | Prometheus text exporter, OTLP/gRPC tracing for observability |
Two protocols, one engine
Section titled “Two protocols, one engine”Both listeners — the native binary protocol and the RESP2 listener — sit in front of the same sharded storage engine. Nothing about the engine assumes which protocol a given connection is using; RESP support is additive, not a separate code path with its own semantics. This design ensures consistency across protocols while maximizing performance.
Sharded, low-contention storage
Section titled “Sharded, low-contention storage”The keyspace is split across N shards (default GOMAXPROCS, configurable via --num-shards) by key hash. Each shard has its
own RWMutex, so operations against different shards proceed without
contending on a single global lock — this is what lets throughput scale
close to linearly as you add cores. The sharded design minimizes lock
contention and maximizes parallelism.
TTL eviction
Section titled “TTL eviction”An active timing wheel (the “chronometer”) walks expiring keys and evicts
them in O(1) per tick, configurable via --evict-interval and
--evict-slots. Lazy eviction on read backs this up, so a key that
expired between ticks is still treated as gone the moment it’s accessed,
even before the chronometer catches up. This dual approach ensures efficient
TTL management without impacting performance.
Persistence
Section titled “Persistence”Tellstone includes a per-shard, append-only write-ahead log (WAL) for
crash recovery. Each shard writes to its own file (shard_000.db,
shard_001.db, …), and every SET and DEL is recorded before it hits the
in-memory engine. On startup, the WAL is replayed to restore the keyspace,
skipping expired keys and applying tombstone deletes.
The WAL is disabled by default. Enable it with --enable-persistence and
optionally set --persistence-dir to choose where the .db files are
written. See Configuration for details.
Transport security
Section titled “Transport security”Both listeners support optional TLS 1.3 transport encryption, served by a forked, single-binary implementation (no external OpenSSL dependency) that wraps gnet connections:
- The binary listener is encrypted implicitly — every connection is TLS from the moment it is accepted.
- The RESP listener has two modes: implicit TLS (the default when certs
are configured) or
STARTTLS, where the listener stays plaintext and each client upgrades the connection in place with aSTARTTLScommand. The upgrade precedes AUTH, so credentials are never sent in the clear. - With
--tls-ca, clients must present certificates (mTLS). - A filesystem watcher automatically rotates certificates, keys, and CAs on disk or Kubernetes projected Secret swaps, with zero downtime.
TLS is off by default. See Security for the full flag semantics, certificate generation, and rotation details.
Role-based access control
Section titled “Role-based access control”RBAC is an optional, protocol-agnostic authorization layer sitting in front of
the storage engine. When --rbac-config points at a policy file, both
listeners switch from the shared --require-pass password to per-user
credentials and role-based command gating:
- A policy snapshot (roles, users, default role) is immutable; writes
build a complete replacement and publish it with one atomic swap, so readers
never block and never see a half-applied policy.
SIGHUPhot-reloads the file the same way. - Each connection resolves its role once at handshake and pins it for its lifetime — a later policy swap never re-evaluates a live session.
- Authorization is fail-closed: no role means deny-all, and a non-matching
key namespace means deny. Denials surface per command as
-NOPERM(or-NOAUTHbefore authentication) rather than by dropping the connection. - The hot path is a single bit test in a command bitset plus a key-prefix scan — single-digit nanoseconds, zero allocations, no locks.
RBAC is off by default. See RBAC for policy files,
rule syntax, and the ROLE command family.
Clustering
Section titled “Clustering”Tellstone runs as a single node today — there is no replication or cluster membership yet. See Clustering & Replication for what’s planned and how to scale without built-in clustering. The architecture is designed to support future distributed features while maintaining the core principles of performance and simplicity.