feat: add to_sealed_dict / from_sealed_dict, fix FastAPI seal omission#16
Merged
bradleygauthier merged 1 commit intomainfrom Mar 18, 2026
Merged
Conversation
to_dict() intentionally excludes seal fields (hash, signature, signed_at, signed_by) because it produces the canonical content used for SHA3-256 hashing. This left API consumers with no way to serialize a complete sealed record without manually extracting attributes via getattr(). Add to_sealed_dict() which returns everything from to_dict() plus the five seal envelope fields. Add from_sealed_dict() as the inverse for roundtrip deserialization. Fix FastAPI integration endpoints (list and get) to use to_sealed_dict() so API responses include the seal. Closes #16
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Capsule.to_sealed_dict()— serializes both canonical content and the cryptographic seal envelope (hash,signature,signature_pq,signed_at,signed_by).to_dict()continues to return only the canonical content used for hashing.Capsule.from_sealed_dict()— inverse ofto_sealed_dict(), restores a complete sealed record from a dict. Enables full roundtrip:seal → to_sealed_dict → from_sealed_dict → verify.GET /capsules/andGET /capsules/{id}) to useto_sealed_dict()so API responses include the seal envelope instead of silently dropping it.Context
to_dict()intentionally excludes seal fields because it produces the canonical representation used for SHA3-256 hash computation — including the hash in the content being hashed would create a circular dependency. However, there was no companion method to serialize the complete sealed record, forcing every API consumer to manually extract seal attributes viagetattr().The FastAPI integration was also using
to_dict()for responses, meaning the endpoints mounted bymount_capsules()were serving capsule records without their cryptographic proof.Changes
capsule.pyto_sealed_dict(),from_sealed_dict(), update docstrings onto_dict()/from_dict()integrations/fastapi.pyto_dict()toto_sealed_dict()test_capsule.pytest_seal.pytest_fastapi.pytest_invariants.pyapi.mdhigh-level-api.mdCHANGELOG.mdpyproject.toml,__init__.py, version tests)Test plan
to_sealed_dict()after realSeal.seal()returns valid crypto fieldscompute_hash(capsule.to_dict())still matchescapsule.hash(seal isolation intact)seal → to_sealed_dict → from_sealed_dict → verifyroundtrip passesto_dict()still excludes seal fields (existing invariant test intact)hash,signature,signature_pq,signed_at,signed_byfrom_sealed_dicttolerates missing seal keys, partial seal keys, and plainto_dict()outputto_sealed_dict()output is JSON-serializable__init__.py, spec/VERSION, CHANGELOG, test assertions all at 1.5.2Closes #16