Hierarchical Resource Configuration with Provenance
Project description
HRCP
Hierarchical Resource Configuration with Provenance
A minimal Python library for hierarchical configuration where you always know where values came from.
The Problem
Configuration in hierarchical systems (orgs → teams → projects, regions → clusters → services) needs:
- Inheritance: Set defaults at the top, override below
- Aggregation: Roll up values from children
- Traceability: Know exactly which node provided each value
HRCP solves this with ~1000 lines of dependency-free Python.
Installation
pip install hrcp
Quick Example
from hrcp import ResourceTree, PropagationMode, get_value
# Build a hierarchy
tree = ResourceTree(root_name="platform")
tree.root.set_attribute("timeout", 30)
tree.root.set_attribute("env", "prod")
tree.create("/platform/us-east/api", attributes={"timeout": 60})
tree.create("/platform/us-east/db")
tree.create("/platform/eu-west/api")
# Inheritance: values flow from ancestors
api = tree.get("/platform/us-east/api")
timeout = get_value(api, "timeout", PropagationMode.INHERIT)
# timeout == 60 (local override)
db = tree.get("/platform/us-east/db")
timeout = get_value(db, "timeout", PropagationMode.INHERIT)
# timeout == 30 (inherited from root)
# Provenance: know where it came from
prov = get_value(db, "timeout", PropagationMode.INHERIT, with_provenance=True)
print(prov.value) # 30
print(prov.source_path) # "/platform" - the root provided this value
Propagation Modes
| Mode | Direction | Use Case |
|---|---|---|
INHERIT |
Ancestors → Resource | Inherit defaults, allow overrides |
AGGREGATE |
Descendants → Resource | Aggregate values, collect metrics |
MERGE |
Ancestors → Resource | Deep-merge dictionaries |
REQUIRE_PATH |
Ancestors → Resource | All ancestors must have truthy values |
COLLECT_ANCESTORS |
Ancestors → Resource | Collect all ancestor values as list |
NONE |
Local only | Get only directly set values |
INHERIT - Inherit from Ancestors
Values cascade from parent to children. Closest ancestor wins.
tree.root.set_attribute("tier", "premium")
tree.create("/org/team/project")
project = tree.get("/org/team/project")
tier = get_value(project, "tier", PropagationMode.INHERIT)
# "premium" - inherited from root
AGGREGATE - Aggregate from Descendants
Collect all values from the subtree.
tree.create("/org/team1", attributes={"headcount": 5})
tree.create("/org/team2", attributes={"headcount": 8})
counts = get_value(tree.root, "headcount", PropagationMode.AGGREGATE)
# [5, 8]
MERGE - Deep Dict Merge
Recursively merge dicts through the hierarchy.
tree.root.set_attribute("config", {"db": {"host": "localhost", "port": 5432}})
tree.create("/org/prod", attributes={"config": {"db": {"host": "prod.db.internal"}}})
prod = tree.get("/org/prod")
config = get_value(prod, "config", PropagationMode.MERGE)
# {"db": {"host": "prod.db.internal", "port": 5432}}
REQUIRE_PATH - All Ancestors Must Enable
Returns value only if ALL ancestors have truthy values. Perfect for opt-in features.
tree.root.set_attribute("feature_enabled", True)
tree.create("/org", attributes={"feature_enabled": True})
account = tree.create("/org/account", attributes={"feature_enabled": True})
# All ancestors enabled → returns True
enabled = get_value(account, "feature_enabled", PropagationMode.REQUIRE_PATH)
# True
# If ANY ancestor is False or missing → returns None
Provenance
The killer feature. Always know where a value came from:
prov = get_value(resource, "timeout", PropagationMode.INHERIT, with_provenance=True)
prov.value # The resolved value
prov.source_path # Path of the resource that provided it
prov.mode # The propagation mode used
For MERGE, provenance tracks which resource contributed each key via prov.key_sources.
Wildcards
Query multiple resources at once:
# * matches one segment
tree.query("/platform/*/api")
# ** matches any depth
tree.query("/platform/**/config")
# Get values across matches
tree.query_values("/platform/*/api", "port", PropagationMode.NONE)
Serialization
# JSON
tree.to_json("config.json")
tree = ResourceTree.from_json("config.json")
# Dict
data = tree.to_dict()
tree = ResourceTree.from_dict(data)
Use Cases
Multi-tenant SaaS
/platform
timeout: 30
/tenant-a # inherits timeout=30
/project-1
/tenant-b
timeout: 60 # override for this tenant
/project-2 # inherits timeout=60
Infrastructure Config
/infra
region: us-east-1
/prod
/api
replicas: 3
/worker
replicas: 5
/staging # inherits region
/api
replicas: 1
Feature Flags
/features
dark_mode: false
/beta_users
dark_mode: true # beta users get dark mode
/user-123 # inherits dark_mode=true
Documentation
Full documentation at hrcp.readthedocs.io, including:
Requirements
- Python 3.11+
- Zero dependencies
Development
# Clone the repository
git clone https://github.com/keongalvin/hrcp.git
cd hrcp
# Install with dev dependencies
uv sync
# Run tests
uv run pytest
# Run linter
uv run ruff check .
# Run benchmarks
uv run python bench/benchmark.py
# Build docs locally
uv run mkdocs serve
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
Key points:
- We follow strict Test-Driven Development (TDD)
- Write failing tests first, then implement
- No mocks or monkeypatching - use pure data models
- Run
uv run pytestanduv run ruff check .before submitting
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hrcp-0.4.0.tar.gz.
File metadata
- Download URL: hrcp-0.4.0.tar.gz
- Upload date:
- Size: 122.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
71ef5dca6c4d1e4ad349aa7c2e11ed04d147666112db63cc35f24e13bd42c741
|
|
| MD5 |
d3ee446453edbbd7d2163a45ce704250
|
|
| BLAKE2b-256 |
3359a445e5d8787fbb29c7ea9d1c72473229b3cb0beec8ae999ae3e65611e682
|
Provenance
The following attestation bundles were made for hrcp-0.4.0.tar.gz:
Publisher:
pypi-package.yaml on keongalvin/hrcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hrcp-0.4.0.tar.gz -
Subject digest:
71ef5dca6c4d1e4ad349aa7c2e11ed04d147666112db63cc35f24e13bd42c741 - Sigstore transparency entry: 866372914
- Sigstore integration time:
-
Permalink:
keongalvin/hrcp@4526e55570d05304cdb067d917ac5e8144efd884 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/keongalvin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-package.yaml@4526e55570d05304cdb067d917ac5e8144efd884 -
Trigger Event:
release
-
Statement type:
File details
Details for the file hrcp-0.4.0-py3-none-any.whl.
File metadata
- Download URL: hrcp-0.4.0-py3-none-any.whl
- Upload date:
- Size: 15.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
05961e9378debf53759d80e7ac927f119f90933c16190daa89bacaef9dffb5eb
|
|
| MD5 |
400097a1295a66de0909e7765b3bad81
|
|
| BLAKE2b-256 |
22c311bdaa7b410007c2ae95224e53cdd7a3a0f1f5812de8bb0f1b66d7f58209
|
Provenance
The following attestation bundles were made for hrcp-0.4.0-py3-none-any.whl:
Publisher:
pypi-package.yaml on keongalvin/hrcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hrcp-0.4.0-py3-none-any.whl -
Subject digest:
05961e9378debf53759d80e7ac927f119f90933c16190daa89bacaef9dffb5eb - Sigstore transparency entry: 866372920
- Sigstore integration time:
-
Permalink:
keongalvin/hrcp@4526e55570d05304cdb067d917ac5e8144efd884 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/keongalvin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-package.yaml@4526e55570d05304cdb067d917ac5e8144efd884 -
Trigger Event:
release
-
Statement type: