haconf
Check Home Assistant YAML and Jinja before you reload — no instance needed.
Status: I1–I8 — the v1 CLI is feature-complete. It walks a configuration
directory, runs a robust YAML loader (duplicate keys, multi-document files,
quoting pitfalls such as on/off/yes/no and number-looking strings,
tab/indentation errors), applies a bundled, versioned integration catalog
(unknown keys under a known integration are errors, integration-name case
mistakes are errors naming the correct form, unknown integrations are
informational only), checks Jinja templates (compiled with Home Assistant's
strict environment shape, rendered against a stub of HA's documented
functions/filters — the real error text surfaces, e.g. No filter named 'strftime', instead of HA core's masked shape error), and runs the I4 integrity
checks (!include/!include_dir_* targets must exist and stay inside the config
dir; !secret keys must be defined in secrets.yaml — values are never read or
printed; packages: entries must not be empty; automation/script
trigger/condition/action shapes; duplicate entity_ids reported once each;
template references to entities neither declared in the config dir nor present in
.storage/core.entity_registry are flagged).
I8 closes the three scopes the audit left open: a homeassistant: packages:
body is checked like the top level (a package name is user-chosen and is never a
key); the nested branches are walked (choose, repeat with its while/until,
parallel, if/then/else, wait_for_trigger, and condition: and/or/not
recursively), so a bare string three levels down is the same error as one at the
top; and custom_templates/*.jinja files are checked as Jinja through one shared
environment, so a macro imported from a sibling file is not an "undefined name".
A script: block is a map of id → script config, in configuration.yaml,
packages: and scripts.yaml alike — the id is a name, the body holds the
options.
I5 completes the contract: every finding carries a severity (error / warning
/ info) and a plain-language hint; warnings and infos pass the run unless
--strict escalates warnings; findings can be suppressed visibly via
.haconfignore or an inline # haconf: ignore=<rule-id> (suppressed findings
are always counted and printed, never hidden); and --json emits one complete
machine-readable object including the suppressed counts. The catalog ships as
package data and is never fetched at runtime.
Release work is complete: packaging I5c (docs/RELEASE.md), the security
pass I6 (scripts/security_sweep.py; the N8–N12 absent-behaviour tests),
and real-config validation I7 (docs/REAL-CONFIG-VALIDATION.md — two pinned
public config repositories; the validation exposed real gaps, all fixed and
pinned by tests/test_i7_fixes.py).
Exit codes: 0 clean (or informational findings only) · 1 findings with at
least one error · 64 usage. The 64 is a deliberate extension of the
contract's 0/1 (deviation D-HC-1, same rationale as RestVerify's D-I1: a
usage failure is not a lint outcome and must not collide with 1).
Read-only by construction: nothing under src/ opens a file for writing, and the
test suite proves it (static AST check + a dynamic open() guard).
Development
pip install -e '.[dev]'
python -m pytest -q # 174 tests, ~2 s
python scripts/security_sweep.py # read-only / no-network / no-eval sweep
python -m build # wheel + sdist in dist/
The same two commands run in CI (.github/workflows/ci.yml) on Python 3.10,
3.11 and 3.12, plus a packaging job that installs the built wheel into a clean
venv and asserts the catalog is inside it while tests/ is not.
License
MIT — see LICENSE.
Metadata
Release files for haconf 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| haconf-0.1.0.tar.gz | 65.5 kB | Without provenance | Details for haconf-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| haconf-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for haconf-0.1.0-py3-none-any.whl |
Total release size: 101.9 kB
Release files / haconf-0.1.0.tar.gz
| Download URL | haconf-0.1.0.tar.gz |
|---|---|
| Size | 65.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d8389f49c2e90f7ce9022a4f195c290ed5797b062919132d931f0f2803960aa0
|
|
BLAKE2b-256 checksum How to use checksums |
e6129438bda1a52638a4a0de449b0db3c683182240753f92df11f370c296ca52
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Release files / haconf-0.1.0-py3-none-any.whl
| Download URL | haconf-0.1.0-py3-none-any.whl |
|---|---|
| Size | 36.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1f7e63c39db2f59945e6f89e5735cdce1ba82c34bd6d5277d7e7b5d8578b9e5a
|
|
BLAKE2b-256 checksum How to use checksums |
67b415a861b687073c512de2c9ea5d5450704c90af97b020a0a750f4a34b683e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Security for haconf 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.