Skip to content

Schema Validation

Elara can validate config content against a JSON Schema attached to a namespace (internal/domain/schema.go, internal/usecase/schema/).

A SchemaAttachment binds one JSON Schema to a (Namespace, PathPattern) pair. The pattern is a glob (using github.com/gobwas/glob with / as separator), so a single schema can cover many configs:

// attach to namespace "prod", pattern "/services/*.json"
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["port"],
  "properties": {
    "port": { "type": "integer", "minimum": 1, "maximum": 65535 }
  }
}

Which schema applies. When more than one attached pattern matches a config path, the most specific pattern wins — specificity is scored by counting wildcard characters (*, ?, [); fewer wildcards is more specific. On a tie, the oldest attachment wins (findBestMatch). Only the single best match is applied to a given config.

When it runs. Validation happens on every config create and update, before the write. other-format configs are skipped (only json and yaml are validated; YAML is re-marshaled through JSON before checking). Schemas are compiled with github.com/santhosh-tekuri/jsonschema v6, which reads the draft from the schema's $schema keyword (through draft 2020-12), and compiled schemas are cached by content hash.

Where it runs. Both write paths: the Web UI/ConnectRPC ConfigService and the etcd-compatible gRPC API. A Put that violates an attached schema is rejected with gRPC InvalidArgument and nothing is stored.

What the etcd path does not do

Schema validation is the only part of the config-management pipeline the etcd write path runs. Content normalization and path validation are deliberately skipped there, because etcd values are opaque bytes — distributed locks, leader-election payloads — and reformatting them would break the byte-for-byte fidelity the wire protocol guarantees. See Reference → etcd API compatibility.

On failure. The write is rejected with a SchemaValidationError carrying a list of violations, each {path, message, keyword}. Its message reads:

schema validation failed: N violation(s): /field: message [keyword]; ...

Over ConnectRPC this maps to an invalid_argument error whose message is the string above, plus a structured SchemaValidationFailure error detail containing every violation's path, message, and keyword (internal/handler/v2/errors.go). See the full mapping in Reference → Errors catalog.