Namespaces & Configs
Namespaces
A namespace is the top-level container for configs. It maps directly to the first path segment of an etcd key (see Configs), so every config lives in exactly one namespace.
Fields (internal/domain/namespace.go):
Name— canonical identifier, validated as a canonical name (used verbatim in etcd keys and RBAC scopes).DisplayName— optional human-friendly label.Description— optional free text.Locked— change-freeze flag (see below).ConfigCount,CreatedAt,UpdatedAt— bookkeeping.
Lifecycle operations (internal/usecase/namespace/): create, update, delete,
lock, unlock.
Locking. Locking a namespace sets Locked = true. While a namespace is
locked, every config write inside it — create, update, delete, both via the
Web/ConnectRPC API and the etcd gRPC API — is rejected with ErrNamespaceLocked.
Use it as a change freeze to protect a whole environment (e.g. prod) from
edits. A locked namespace also cannot be deleted; you must unlock it first.
Delete guard. A namespace can only be deleted when it is unlocked and empty. Deleting a namespace that still contains one or more configs fails with a validation error reporting the config count — Elara never cascades a namespace delete into its configs.
Configs
A config is a single key/value entry inside a namespace
(internal/domain/config.go).
Fields that matter:
Path— must start with/, must not contain//, and must not end with/(e.g./services/api.yaml).Content— the config body, stored normalized.Format—json,yaml, orother. Parsed viaParseFormat(ymlis accepted as an alias foryaml); auto-detected from the path extension (.json/.yaml/.yml, elseother) when not set. Format is immutable on update — you cannot change a config's format after creation.ContentHash— SHA-256 of the content, used to detect no-op writes.Version— per-config counter, starts at1and increments on each write.Revision/CreateRevision— the global revision at last modify (etcdmod_revision) and at first create.Metadata— arbitrary string map.Locked/NamespaceLocked— this config's own lock, and whether its namespace is locked.
etcd key mapping. A config is addressable over the etcd gRPC API as
/{namespace}{path}, where the path keeps its leading slash
(internal/handler/etcdv3/keyencoding.go):
namespace "prod" + path "/services/api.yaml" → /prod/services/api.yaml
namespace "default" + path "/foo.json" → /default/foo.json
The first path segment after the leading / is the namespace; everything from
the second / onward (leading slash included) is the config path. See
Reference → etcd API compatibility for
the full RPC support matrix.
History. Every write appends a revision-stamped entry to the history
bucket. You can list a config's history and read it as it existed at any past
revision (internal/usecase/config/service_history.go, GetKVAtRevision) —
this is what backs etcd point-in-time reads and watch-from-revision. See
Watch & Revisions.
Config locking. Individual configs can be locked and unlocked
(internal/usecase/config/service_update.go). While a config is locked, any
attempt to update or delete it is rejected with ErrLocked — enforced in
storage, so it holds for both the UI and the etcd API (the etcd Put/
DeleteRange paths return the same error). Lock/unlock transitions are recorded
in a lock-history bucket. Over ConnectRPC a locked write surfaces as a
failed_precondition error. Locking a namespace has the same effect on all of
its configs (NamespaceLocked).