Skip to content

etcd API compatibility

Elara serves the etcd v3 gRPC API on port 2379 (internal/handler/etcdv3/). Any etcd v3 client — etcdctl, clientv3, or an SDK in another language — connects unchanged, provided it only uses the RPCs below.

Key encoding

A config is addressable as /{namespace}{path}, where path keeps its leading slash:

namespace "prod" + path "/services/api.yaml"  →  /prod/services/api.yaml

The first path segment after the leading / is the namespace; everything from the second / onward is the config path (internal/handler/etcdv3/keyencoding.go). See Namespaces & Configs for the underlying domain fields this maps to.

Supported RPCs

KV (kv_server.go)

RPC Support Notes
Range ✅ Full Prefix ranges (range_end) and full-keyspace scans work; historical reads by revision are supported.
Put ✅ Writes a config. Runs JSON Schema validation against the namespace's attached schema — a violation is rejected with InvalidArgument and nothing is stored (see Schema Validation). Rejected with FailedPrecondition if the target config or its namespace is locked. ignore_value is rejected with Unimplemented.
DeleteRange ✅ Supports PrevKv to return deleted values.
Txn ⚠️ Partial Compare-and-swap style transactions work, but the transaction is not fully atomic with the rest of the write path yet — a multi-op Txn is not guaranteed all-or-nothing under concurrent writes the way real etcd's is. Don't rely on it for correctness-critical multi-key transactions today.
Compact ⚠️ No-op Accepted and returns success, but Elara never truncates history — there is nothing to compact. Safe to call (e.g. from a client library that compacts periodically); it just does nothing.

Watch (watch_server.go)

RPC Support Notes
Watch ✅ Full Bidirectional streaming, CreateRequest/CancelRequest, StartRevision for resumable watches (history replay then live), PrevKv. See Watch & Revisions.

Maintenance (maintenance_server.go)

RPC Support Notes
Status ⚠️ Synthetic Returns a response shaped like real etcd's (current revision, a static version string, a fixed member/leader id) so clients that check it don't break, but there's no raft cluster behind it — the numbers describe a single-node stand-in.
Alarm ⚠️ Always empty Accepted; always returns no alarms. Elara has no equivalent of etcd's NOSPACE/CORRUPT alarms.
Snapshot ❌ Unimplemented No online backup RPC. See Troubleshooting → Backup & Restore.
Defragment, Hash, HashKV, MoveLeader, Downgrade ❌ Unimplemented Not applicable to a single bbolt-backed instance.

Cluster (cluster_server.go)

RPC Support Notes
MemberList ⚠️ Synthetic Returns a single synthetic member so clients that call it (some SDKs do, for endpoint discovery) don't break.
MemberAdd, MemberRemove, MemberUpdate, MemberPromote ❌ Unimplemented Elara is single-instance; there is no cluster to reconfigure.

Explicitly unsupported

API Status Why
Lease (LeaseGrant, LeaseKeepAlive, LeaseRevoke, LeaseTimeToLive, LeaseLeases) ❌ Not registered at all No LeaseServer is wired up — key TTLs and lease-based locks/elections that depend on leases (e.g. etcd concurrency package election recipes) will not work against Elara.
Auth API (etcd's own AuthEnable/UserAdd/RoleGrantPermission/…) ❌ Not applicable Elara has its own token-based auth for this port — see Client Auth (etcd) — not etcd's built-in RBAC. Don't confuse the two.

Authentication and error mapping

Where the write path diverges

A key written through Put is stored differently from the same key written through the ConnectRPC ConfigService:

  • Content is stored byte-for-byte. The normalization the management path applies (re-marshaling, canonical formatting) is deliberately skipped, so a value round-trips exactly as written. etcd values are frequently not structured config at all — distributed locks, leader-election payloads — and rewriting them would break clients that compare bytes.
  • Path validation is skipped. A key written over etcd may carry a path the ConnectRPC API would reject.

Schema validation is the one management-path rule that does apply — see the Put row above.

Requests are authenticated via a Bearer service token in gRPC metadata (or unauthenticated if client.auth.enabled=false) — see Sessions & Tokens → Tokens. Unlike the ConnectRPC API's structured error codes, the etcd wire protocol returns plain gRPC status codes: Unauthenticated for a missing/invalid/expired token, PermissionDenied for a namespace or role check failure, FailedPrecondition for a locked config/namespace, and InvalidArgument for a value that fails the namespace's attached JSON Schema. See Reference → Errors catalog for the full domain-error mapping.