Repository navigation
feat(promote): bind HMAC v2 tokens to config hash and inventory - #25
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Reviewer's GuideAdds backward-compatible HMAC v2 approval tokens bound to canonical grounded configs and target inventory, threads optional live inventory through promotion gates and records, and fails closed on config or inventory drift while retaining v1 and unverified-community behavior. Sequence diagram for HMAC v2 promotion approvalsequenceDiagram
participant Caller
participant Promote as promote
participant Gate as gate.evaluate
participant Tokens as verify_approval
participant HMAC as HMAC key
Caller->>Promote: promote(bundle, approval_token, live_inventory_sha256)
Promote->>Gate: evaluate(bundle, approver, approval_token, live_inventory_sha256)
Gate->>Gate: config_digest(bundle)
Gate->>Gate: inventory_digest(bundle) or live_inventory_sha256
Gate->>Tokens: verify_approval(approver, token, bundle_sha256, config_sha256, inventory_sha256)
Tokens->>HMAC: Validate HMAC-SHA256
HMAC-->>Tokens: Valid signature
Tokens-->>Gate: Approval accepted or drift denied
Gate-->>Promote: GateDecision
Promote-->>Caller: Promotion record with live_inventory_sha256
Flow diagram for v2 token binding and drift denialflowchart TD
A[mint_token_for_bundle] --> B[digests_from_bundle]
B --> C[Bundle SHA-256]
B --> D[config_digest]
B --> E[inventory_digest]
C --> F[HMAC v2 token]
D --> F
E --> F
F --> G[verify_approval]
G --> H{Bundle, config, and inventory match?}
H -->|yes| I[Approval accepted]
H -->|no| J[Promotion denied]
K[Optional live_inventory_sha256] --> G
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 issue
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="core/promote/tokens.py" line_range="192-193" />
<code_context>
+
+ cfg = (config_sha256 or "").strip().lower() or None
+ inv = (inventory_sha256 or "").strip().lower() or None
+ if version is None:
+ version = 2 if (cfg or inv) else 1
+ if version not in (1, 2):
+ raise TokenError("token version must be 1 or 2")
</code_context>
<issue_to_address>
**issue (broader_impact):** The existing `/api/approve/mint` caller still invokes `mint_token()` without config or inventory claims, so it continues issuing v1 tokens. G2/G3 deliberately accept those v1 tokens without checking config or inventory, leaving the public approval flow unable to enforce the feature's stated binding.
**Triggers:** When approvals are minted through the existing server API rather than by directly calling `mint_token_for_bundle()`.
**Suggested fix:** Update the API mint flow to accept the bundle (or its config and inventory digests) and call `mint_token_for_bundle()`/`mint_token(..., version=2)`; update promotion input handling to pass the live inventory digest.
</issue_to_address>Sourcery assessment
Needs a human reviewer. 1 finding to address first, and if the new HMAC claims are validated incorrectly, an approval token could authorize promotion of configurations or inventory different from what the approver reviewed, allowing changes to reach the live connector. Reverting would stop future misuse but would not undo any production changes already promoted.
Blocking findings: core/promote/tokens.py:193
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Close the #24 API gap: POST /api/approve/mint with a bundle now mints v2, promote accepts a live inventory_sha256, and v2 verification fails closed on missing claims. Hash-only mint stays v1. Co-authored-by: georgy75 <georgy75@gmail.com>


Why
Closes #24. External review (@ThorCommerce): Bind the token to the config hash and target inventory version. An approved token shouldn't survive a change to either one.
Phase 3 v1 tokens are bound to approver + bundle sha256 + expiry. That kills a token when the sealed bundle changes. It does not give G2/G3 a first-class config or inventory claim, and it does not refuse an approved token after the live target inventory has moved.
What landed on this branch
core/promote/tokens.py— v2 payload{a, b, c, inv, exp, n, v:2}.c= SHA-256 of canonical grounded configs.inv= SHA-256 of twin topology + engine + node_count + device list + optionaltwin.inventory_rev.mint_token_for_bundle()emits v2. Legacymint_token(approver, bundle_sha256)still emits v1.core/promote/gate.py— G2/G3 pass config + inventory digests intoverify_approval. Optionallive_inventory_sha256overrides the bundle-derived inventory fingerprint.core/promote/promote.py—live_inventory_sha256threaded through; recorded on the promotion record when supplied.v1 tokens keep verifying. Unknown token versions fail closed. Community/sim with no
AEGIS_APPROVE_KEYstaysasserted-unverified.Honesty
This is not YubiKey PIV and not a live CMDB connector. The inventory fingerprint is derived from the twin record in the bundle unless the caller supplies a live digest at promote time.
Tests already run locally (not all pushed yet)
python -m aegis.tests.tokens_test— PASS 33 checks (v1 + v2)python -m aegis.tests.promote_test 200— PASS (P8 v1 + v2 + live inventory drift deny)python -m aegis.tests.api_test— PASS 48 checksExisting v1 CI suites on this branch should stay green because v1 mint/verify paths are unchanged and extra gate hashes are ignored for v1 tokens.
Follow-up commit still needed from the working tree:
tests/tokens_test.py,tests/promote_test.py,tests/api_test.py,serve.py(bundle mint → v2, promote acceptsinventory_sha256), CHANGELOG / DEVELOPER / PHASES / Pages.Test plan
v0.2.1after merge so the X reply can point at a releaseSummary by Sourcery
Bind approval tokens to grounded configuration and target inventory state so promotions cannot reuse approvals after either changes.
New Features:
Bug Fixes:
Enhancements:
Documentation:
Tests: