HERY Concepts¶
HERY (Hierarchical Entity Relational YAML) is the data foundation of the Amadla ecosystem. It extends YAML with entity management capabilities, combining concepts from relational databases and package managers.
Core Model¶
| HERY Term | Analogy | Description |
|---|---|---|
| Entity Type | Table schema | A versioned type with a JSON Schema (e.g., application@v1.0.0) |
| Entity Instance | Row | A specific .hery document with actual data |
File Format¶
HERY files use the .hery extension and are valid YAML. A file may contain multiple YAML documents separated by ---, each representing an independent entity instance. However, if an element needs to be individually addressable via _extends, it must be its own file — multi-document YAML is only for anonymous accumulation (e.g., multiple firewall rules in one file).
An optional yaml-language-server comment enables IDE validation (VS Code, JetBrains, Vim/Neovim). The authoritative type declaration is the _type property. Tooling (hery fmt) can auto-generate the comment.
Reserved Properties¶
HERY defines five reserved properties, recognized only at the document root.
_type — Type (required)¶
Declares the entity type and version. Determines which JSON Schema validates this document. hery resolves the URI to fetch the entity type (schema + defaults).
Versions follow semver and map directly to Git tags (e.g., @v1.0.0 → git tag v1.0.0). Use @latest for the most recent tag. Both vanity URIs (amadla.org/entity/...) and direct Git URIs (github.com/...) are supported.
When used with _extends, _type and _extends serve different roles: _type declares the schema (what this entity is), _extends declares data inheritance (who it inherits from). When only _extends is present, _type is inherited from the extended entity. When both are present and they conflict, hery warns but allows it (e.g., for schema version upgrades).
_extends — Extends (optional)¶
Inherit values from another entity instance of the same type. The extended entity's _body, _meta, and _requires values are deep-merged as defaults — the child only specifies overrides. _extends is purely a data/merge operation — it does not imply execution ordering.
_extends targets specific elements using the filename as a path segment in the URI:
This overrides just the database.hery element from the WordPress entity. The filename is the element identity — one addressable element = one file.
If the extended entity defines protocol: tcp and the child doesn't override it, the merged result includes protocol: tcp. Extends chains can cascade transitively. Cycles are detected and rejected.
_meta — Metadata (optional)¶
Metadata for filtering, searching, and categorizing entities. Structure defined by the entity's schema.
_meta:
name: My Application
description: Production web server configuration
tags: [production, web]
_body — Content (optional)¶
Contains the entity's data. Validated against the entity's JSON Schema.
When _body is omitted, the entity inherits all defaults via _extends (if any).
_requires — Dependencies (optional)¶
Declares hard dependencies on other entities. Used by amadla to build a dependency graph (DAG) and determine execution order via topological sort.
_requires:
- ./database.hery # local file (same directory)
- github.com/SomeOrg/base-infra/database.hery@v1.0.0 # external file (specific version)
- amadla.org/entity/application/db/rdbms@^v1.0.0 # type URI (any entity of this type)
_requires supports three reference forms:
- Local file path (
./database.hery) — requires a specific file in the same directory. Must be relative (no absolute paths). Paths are sandboxed to the entity directory (no../escape). - External file path (
github.com/SomeOrg/base-infra/database.hery@v1.0.0) — requires a specific file from another repository. Version follows the same rules as_type(@v1.0.0exact,@^v1.0.0compatible range,@latest). - Type URI (
amadla.org/entity/application/db/rdbms@^v1.0.0) — "at least one entity of this type must exist and be processed before me." Useful when you want to include a base type and then provide your own local entity of the same type to override it.
_requires is orthogonal to _extends: _extends handles data inheritance (merge), _requires handles execution ordering. Use both when you need inheritance AND ordering.
hery validates _requires syntax at parse time. amadla validates that referenced entities actually exist at orchestration time.
Note: _requires declares entity-level dependencies. Application-specific dependencies (like podman-compose depends_on) go in _body as require — these are handled by the relevant tool (e.g., lay), not amadla.
Reservation rules:
_-prefixed keys are reserved at the document root only- Inside
_bodyand_meta,_-prefixed keys are regular data (e.g.,_idfor MongoDB documents) - The five reserved properties cannot appear inside
_bodyor_meta
Entity Composition (via Schema)¶
Sub-entities are not nested inside _body with HERY markup. Instead, the entity's JSON Schema uses standard $ref to declare which parts of _body correspond to other entity types:
_type: amadla.org/entity/application/webserver@v1.0.0
_body:
server_name: localhost
port: 443
database:
engine: postgres
port: 5432
The WebServer schema declares that database conforms to the DB entity schema via $ref. Downstream tools (weaver plugins, judge) follow $ref to understand entity boundaries — no custom markup needed.
Deep Merge Model¶
When values are merged (via _extends inheritance or layer composition), deep merge applies to _body, _meta, and _requires. _type is never merged — it is either set explicitly or inherited as-is from the extended entity.
- Objects: Merge recursively. Child values override, extended-entity-only keys preserved.
- Arrays: Replace — if the child defines an array, it replaces the extended entity's entirely (this means a child's
_requireslist replaces the extended entity's entirely). - Scalars: Child value wins.
All layers are preserved in the cache. The merged view shows the deep-merged result, but individual layers can be queried independently.
Entity Type Definition¶
An entity type is a directory (typically in a Git repo) with a visible schema at its root:
No hidden directories. The schema is the primary artifact — visible and discoverable. hery reads all .hery files in the directory. One directory = one entity type.
Entity schemas extend the base HERY schema (amadla.org/entity/hery@v1.0.0) which defines the five reserved properties. Schemas use $ref to compose sub-entity schemas, enabling downstream tools to understand nested entity boundaries.
URI Resolution¶
HERY resolves _type and _extends URIs using a Go-module-inspired algorithm:
- Known hosts (github.com, gitlab.com): convention-based —
host/owner/repo(3 segments) is the repo root, remainder is path within repo - Custom domains (amadla.org): meta tag discovery — hery sends
GET https://host/path?hery-get=1and reads a<meta name="hery-import">tag to find the actual Git repo
Example meta tag:
<meta name="hery-import" content="amadla.org/entity/application git https://github.com/AmadlaOrg/Entities/Application">
This enables vanity URIs (amadla.org/entity/network → github.com/AmadlaOrg/Entities/System/Network), host migration without breaking references, and can be served as static HTML.
Storage Layout¶
Project level¶
my-project/
webserver.hery # Entity instances (committed)
network.hery # Entity instances (committed)
hery.lock # Lock file (committed)
.hery.cache # SQLite cache (gitignored, derived)
Global cache¶
~/.cache/hery/
entity/ # Resolved entity types
amadla.org/entity/application@v1.0.0/
schema.hery.json
default.hery
Disposable — hery re-fetches from Git when needed.
Lock Files¶
hery.lock (JSON format) at the project root:
- Same data as
.hery.cachein a portable format - Committed to Git for reproducibility
- Faster subsequent merges
- Should not be edited manually
Querying¶
hery uses a two-stage query model:
- Selection — CLI flags filter which entities are returned (backed by SQLite indexes)
- Transformation — optional jq expressions reshape the output (via gojq, compiled in)
# All entities
hery query
# Filter by type (glob)
hery query --type 'amadla.org/entity/application@v*'
# Filter by meta tag
hery query --tag production
# Filter + extract fields with jq
hery query --type '*/network@*' --jq '.[].\_body.port'
# Combine with external tools (UNIX pipe)
hery query --type '*/application@*' | doorman inject | weaver render
Selection flags (--type, --meta, --tag) hit SQLite indexes for fast filtering. The --jq flag applies jq transformations without requiring jq to be installed. Users can also pipe to external jq directly.
Results are always JSON, designed for piping to downstream tools. All layers are preserved in the cache — use --layers to inspect individual layers before merge.
Entity Versioning¶
Entity versions map directly to Git tags. The @version in a type URI corresponds to a git tag on the entity type repository. For example, amadla.org/entity/application@v1.0.0 resolves to the git tag v1.0.0.
amadla.org/entity/application@v1.0.0 # → git tag v1.0.0
amadla.org/entity/application@latest # → most recent git tag
Version pinning ensures reproducible deployments.