Skip to content

feat(contextgraph-types): implement record_hash and RecordAttestation - #114

Merged
macanderson merged 5 commits into
mainfrom
feat/record-hash-attestation-b726a4bc
Aug 30, 2026
Merged

feat(contextgraph-types): implement record_hash and RecordAttestation#114
macanderson merged 5 commits into
mainfrom
feat/record-hash-attestation-b726a4bc

Conversation

@macanderson

@macanderson macanderson commented Aug 30, 2026

Copy link
Copy Markdown
Owner

Pull request

Summary

Implements the lifecycle profile's record_hash (RFC 8785 JCS + the omit-self
rule) and RecordAttestation verification (detached Ed25519), and publishes
golden vectors so profile LF1 becomes a true claim. Closes #96.

What changed

The library — contextgraph_types::record_attest (new module, the record-layer
sibling of attest):

  • record_hash(&Value) / record_hash_preimage(&Value) / record_hash_of(&ContextRecord)
    / record_hash_is_current(&Value). The preimage is exposed as well as the digest
    because a digest cannot tell an implementer where their canonicalization diverged.
  • sign_record, sign_record_attestation, verify_record_attestation,
    verify_signed_record_hash, record_attestation_message. Verdicts reuse the
    frame layer's AttestationVerdict — a shared result vocabulary, while the two
    attestation types stay distinct as ADR 0010 argued.
  • Two new off-by-default features: record-hash (serde_json,
    serde_json_canonicalizer) and record-attestation (record-hash +
    attestation). The default dependency set is still serde only.

Decisions (recorded in ADR 0017):

  • Omit-self: removed, not blanked. LH1 says removed, and the ADR records
    what that buys: a record hashes identically whether the member is absent,
    correct, or wrong, so a producer never invents a placeholder and a verifier
    never has to know which one was chosen. Only the top-level member is removed —
    a record_hash inside extensions is content and stays in the preimage.
  • JCS is delegated, not hand-rolled. serde_json_canonicalizer 0.3.2 (MIT,
    ~6M downloads, last published 2026-02), whose number formatting goes through
    ryu-js (Apache-2.0 OR BSL-1.0), the Boa engine's ECMAScript formatter. It was
    already a dev-dependency of the conformance suite; this promotes it to a pinned
    workspace dependency so the library and the suite checking it cannot
    canonicalize with two different versions. Both licenses are compatible with the
    workspace's MIT OR Apache-2.0.
  • The signature is domain-separated (new normative LC4): the signed message
    is "contextgraph/attest/1/record" followed by the digest's 32 raw bytes. A
    frame commitment is domain-bound by construction; a record_hash is a plain
    SHA-256 over a JSON document that anyone else can also compute, so signing it
    raw would make one signature mean whatever the presenter says it means. Additive
    LC3 named no preimage because nothing had implemented signing.
  • Verification recomputes the hash rather than reading the stored member (new
    normative LC5), so the obvious laundering move — edit the record, then rewrite
    record_hash so it is internally consistent again — is a CommitmentMismatch.

Vectors:

  • tests/fixtures/record-hash-vectors.json — the canonical JCS text of every
    record fixture beside its hash.
  • tests/fixtures/record-attestation.json — a real Ed25519 signature. It
    previously carried 49 bytes of DER-shaped filler, in the directory LF1 calls
    the canonical home for the profile's golden vectors.
  • tests/fixtures/record-attestation-key.json — the published test key (seed,
    public key, signed message), labelled as forgeable everywhere it appears.
  • contextgraph-types/tests/record_vectors.rs — the same values inline, so they
    travel inside the published crate.

Checking:

  • The conformance suite now recomputes everything through the library rather
    than a private copy of the rule, and verifies the attestation under its key.
  • schema/validate-examples.py checks, in Python and with no JCS library, that
    each published canonical text hashes to its published digest and parses back to
    the fixture with record_hash removed. It deliberately does not claim
    Python canonicalizes like RFC 8785; the profile prose that implied it did is
    corrected.
  • A new features CI job builds and runs each feature combination. Every feature
    this crate has is off by default and no job turned any of them on, so the
    attestation code from Sign the provenance chain, open the FrameKind vocabulary #87 compiled nowhere in CI.

Corrections found on the way: LH2 said JCS sorts object members by code
point. RFC 8785 §3.2.3 sorts by UTF-16 code unit, and the two orders differ
for a supplementary character (its lead surrogate sorts below U+E000) — the new
test demonstrates it. The profile and fixtures README also claimed a Python
json.dumps canonicalizer reproduces the vectors byte-for-byte; that holds for
these fixtures because their keys are ASCII and their numbers round-trip
identically, and the prose now says so instead of overclaiming.

Evidence

RFC 8785 conformance against the RFC's own published vectors (from
https://www.rfc-editor.org/rfc/rfc8785.txt, cited in the tests):

  • §3.2.4's hexadecimal byte listing for the specification's worked example,
    asserted as bytes.
  • §3.2.3's property-sorting test data and the expected order the RFC states.
  • Appendix B Table 1: every IEEE 754 bit pattern and its required ECMAScript
    text — -00, 5e-324, 1.7976931348623157e+308, the 1e+21 and
    0.000001 exponent thresholds, the round-to-even sample.

The twelve fixtures' record_hash values are unchanged by this PR. That is
the evidence this implements the existing rule rather than redefining it: the
library reproduces byte-for-byte what the suite's private helper computed.

Witness — fails without the change, passes with it. Two observations, both
run in this worktree:

  1. The attestation vector. With origin/main's record-attestation.json
    restored into the new tree:

    test the_attestation_example_verifies_under_its_published_key ... FAILED
      left: MalformedSignature
     right: Valid
    

    Restoring the new fixture: test result: ok. 1 passed.

  2. The library API. git stash push of the tracked source changes (both
    Cargo.tomls, src/lib.rs, src/record.rs), leaving the new tests in place:

    error[E0432]: unresolved import `contextgraph_types::record_attest`
    error[E0425]: cannot find function `record_attestation_message` in crate `contextgraph_types`
    

    After git stash pop: test result: ok. 8 passed (conformance) and
    test result: ok. 5 passed (record_vectors).

    One honest caveat: record_vectors.rs carries a file-level
    #![cfg(feature = "record-attestation")], matching attestation_vectors.rs,
    so on the old tree it compiles to zero tests and reports green rather than
    failing. That vacuous-green shape is exactly what the new features CI job
    defends against, by naming each feature explicitly.

Other commands run (all in the worktree, scoped per SCR-001):

  • cargo test -p contextgraph-types --all-features — 153 + 5 + 2 + 5 + 4 passed.
  • cargo test -p contextgraph-conformance --test lifecycle_profile_examples — 8 passed.
  • cargo test -p contextgraph-conformance --test golden_fixtures — 10 passed.
  • cargo clippy -p contextgraph-types with each of (none), attestation,
    record-hash, record-attestation, --all-features, --all-targets -D warnings — clean.
  • cargo clippy -p contextgraph-conformance --all-targets -- -D warnings — clean.
  • cargo fmt -- --check — clean.
  • python3 schema/validate-examples.py — OK, all examples validate.
  • python3 .github/scripts/check-deploy-hygiene.py — OK.
  • cargo metadata --locked — lock in sync.

Follow-up pushes

Sourcery

Sourcery posted a review guide and an assessment table against #96 on the first
head (85fe829): all three objectives , no , and no inline comments. It has
not re-reviewed the two follow-up commits, which are the import gate, the ADR
rename and the index rows.

Residue filed

Checklist

  • One logical change per PR (smaller lands faster)
  • Gate is green locally — fmt, clippy -D warnings, test (scoped to the
    touched crates per SCR-001; the workspace run is CI's job)
  • A witness test is included, or a reason there isn't one is stated below
  • Docs updated in the same PR if behavior or flags changed (README.md,
    docs/, doc comments, --help text)
  • All commits signed off (git commit -s, DCO)
  • CHANGELOG.md updated under [Unreleased] if user-visible

Registry submission (only if adding a row to docs/registry.md)

  • Not applicable — this PR does not add/change a conformance registry entry

Protocol-stability impact (if a spec/wire change)

  • Additive (new optional field/check) — safe within contextgraph/1

LC4 and LC5 are new normative rows in the lifecycle profile, which is a
draft (contextgraph/lifecycle/1.0-draft) and separate from the frozen
contextgraph/1.0 core. They pin a preimage that had no stated definition and
that nothing had implemented, so no deployed provider can be signing something
else today. The record wire shape, the record schema, and every fixture's
record_hash are unchanged. LH2's correction from "code point" to "UTF-16
code unit" changes no fixture's bytes; it makes the prose match what RFC 8785
requires and what the reference implementation already did.

No new deleted tests.

License

By submitting this pull request, I agree to dual-license this contribution
under MIT OR Apache-2.0, as certified by my DCO sign-off.

Closes #96

The lifecycle profile has always defined `record_hash` as the sha256 over
the RFC 8785 (JCS) canonicalization of a record with its own `record_hash`
removed (LH1), and `RecordAttestation` as a detached Ed25519 signature over
it (LC3). Both were prose and a struct. The only hashing code in the
workspace was a private helper inside the conformance suite, so the suite
proved the fixtures agreed with the suite; and the attestation fixture
carried 49 bytes of DER-shaped filler where a signature belongs, with no
key published, so no implementation could reproduce or refute it.

`contextgraph_types::record_attest` makes the rule callable, behind two new
off-by-default features. `record-hash` adds RFC 8785 canonicalization
(delegated to serde_json_canonicalizer, whose numbers route through ryu-js
— JCS number serialization is ECMAScript Number::toString, and its exponent
thresholds are where reimplementations diverge in silence).
`record-attestation` adds Ed25519 on top. A frame-only consumer pays for
neither, and the crate's zero-dependency default is unchanged.

The signed message is domain-separated: "contextgraph/attest/1/record"
followed by the digest's 32 raw bytes. A frame commitment is domain-bound
by construction; a record_hash is a plain SHA-256 over a JSON document that
any number of unrelated systems also compute, so signing it raw would let
one signature mean whatever the presenter says it means. Verification
recomputes the record's hash rather than reading the stored member, so
editing a record and rewriting its hash to match a stolen signature is
caught as a mismatch instead of passing.

Evidence: the canonicalizer is checked against RFC 8785's own vectors —
§3.2.4's byte listing, §3.2.3's sorting data, and Appendix B's IEEE 754
number table. The twelve fixtures' hashes are unchanged, which is what
shows this reproduces the existing rule rather than redefining it.
tests/fixtures/ now publishes the canonical preimage text of every fixture,
a real signature, and the test key that produced it; the conformance suite
recomputes all of it through the library. schema/validate-examples.py
checks the vectors from Python, without a JCS library, so an implementer
who has neither Rust nor a canonicalizer can still rely on them.

A CI job builds and runs each feature combination — until now every feature
this crate has was off by default and no job turned any of them on, so the
attestation code added in #87 compiled nowhere in CI.

Also corrects LH2, which said JCS sorts members by code point. RFC 8785
§3.2.3 sorts by UTF-16 code unit, and the two orders differ for a
supplementary character.

Closes #96

Signed-off-by: macanderson <mac@oxagen.sh>

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @macanderson, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 15 hours and 54 minutes by commenting @sourcery-ai review. Upgrade to get a review now.

@sourcery-ai

sourcery-ai Bot commented Aug 30, 2026

Copy link
Copy Markdown

Reviewer's Guide

Implements lifecycle-profile record hashing and detached Ed25519 attestation as optional contextgraph-types features, with domain-separated verification, RFC 8785 conformance evidence, reproducible golden vectors, cross-language validation, updated protocol documentation, and CI coverage for each feature combination.

Sequence diagram for record hashing and detached attestation verification

sequenceDiagram
    participant Provider
    participant RecordAttest
    participant Canonicalizer
    participant Verifier
    participant Ed25519

    Provider->>RecordAttest: record_hash(record)
    RecordAttest->>Canonicalizer: JCS(record without top-level record_hash)
    Canonicalizer-->>RecordAttest: canonical preimage
    RecordAttest-->>Provider: sha256:<hex>
    Provider->>RecordAttest: sign_record(record, signing_key_seed)
    RecordAttest->>RecordAttest: record_attestation_message(record_hash)
    RecordAttest->>Ed25519: sign(domain tag + raw digest)
    Ed25519-->>Provider: detached RecordAttestation
    Verifier->>RecordAttest: verify_record_attestation(record, attestation, public_key)
    RecordAttest->>Canonicalizer: recompute JCS hash
    Canonicalizer-->>RecordAttest: expected record hash
    RecordAttest->>Ed25519: verify(domain tag + raw digest)
    Ed25519-->>Verifier: AttestationVerdict
Loading

Flow diagram for omit-self record hashing

flowchart TD
    Input["Record JSON value"] --> Object{"Top-level object?"}
    Object -- No --> Error["RecordHashError::NotAnObject"]
    Object -- Yes --> Remove["Remove top-level record_hash"]
    Remove --> Keep["Keep nested record_hash members"]
    Keep --> JCS["RFC 8785 canonicalization"]
    JCS --> SHA["SHA-256 canonical bytes"]
    SHA --> Digest["sha256:<64 lowercase hex>"]
Loading

File-Level Changes

Change Details Files
Adds RFC 8785 record content addressing with an omit-self preimage rule.
  • Introduces record hashing, preimage, typed-record, and freshness APIs.
  • Removes only the top-level record_hash member before canonicalization.
  • Delegates JCS canonicalization and SHA-256 behind the off-by-default record-hash feature.
  • Adds RFC 8785 conformance coverage, including UTF-16 sorting and number-format vectors.
contextgraph-types/src/record_attest.rs
contextgraph-types/Cargo.toml
contextgraph-types/src/lib.rs
contextgraph-types/tests/record_vectors.rs
contextgraph-conformance/tests/lifecycle_profile_examples.rs
Cargo.toml
Cargo.lock
Implements detached, domain-separated Ed25519 record attestation signing and verification.
  • Adds signing, message construction, and verification APIs with shared AttestationVerdict results.
  • Signs the record domain tag followed by the hash's 32 raw bytes.
  • Recomputes record content during verification and reports commitment mismatches before signature failures.
  • Extends RecordAttestation with constructors and wire-validation helpers while preserving its distinct type.
contextgraph-types/src/record_attest.rs
contextgraph-types/src/record.rs
contextgraph-types/src/lib.rs
contextgraph-types/Cargo.toml
contextgraph-types/tests/record_vectors.rs
Publishes reproducible golden vectors and moves lifecycle conformance checks onto the library implementation.
  • Publishes canonical JCS preimage text and hashes for all lifecycle fixtures.
  • Replaces placeholder attestation data with a real signature and publishes its forgeable test key and signed message.
  • Adds inline crate vectors for hashes, preimages, keys, signatures, and tamper cases.
  • Adds Python cross-checks for vector coverage, digest derivation, omit-self reconstruction, and signed-message construction.
tests/fixtures/record-hash-vectors.json
tests/fixtures/record-attestation.json
tests/fixtures/record-attestation-key.json
contextgraph-types/tests/record_vectors.rs
contextgraph-conformance/tests/lifecycle_profile_examples.rs
schema/validate-examples.py
tests/fixtures/README.md
Documents the record-layer protocol decisions and validates feature coverage in CI.
  • Records the JCS, omit-self, domain-separation, verification, feature-split, and vector decisions in ADR 0012.
  • Updates lifecycle requirements and fixture documentation with LC4, LC5, and corrected UTF-16 sorting semantics.
  • Adds off-by-default feature documentation and a CI matrix for individual feature combinations.
  • Updates the changelog with the new public APIs, vectors, and conformance behavior.
docs/adr/0012-record-hash-and-record-attestation.md
docs/profiles/context-exchange-provider.md
contextgraph-types/README.md
tests/fixtures/README.md
.github/workflows/ci.yml
CHANGELOG.md

Assessment against linked issues

Issue Objective Addressed Explanation
#96 Implement callable RFC 8785 JCS record hashing in contextgraph-types, including the documented top-level omit-self rule and exposure of the canonical preimage bytes.
#96 Implement detached Ed25519 RecordAttestation signing and verification with a normatively defined signed message, recomputation of the record hash during verification, RFC conformance tests, and reproducible golden record and attestation vectors.
#96 Make the implementation part of the documented and continuously tested project surface, including feature-gated CI coverage, library-based conformance checks, ADR/profile/README/changelog updates, and witness-test evidence.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions

github-actions Bot commented Aug 30, 2026

Copy link
Copy Markdown

SCR-003 DoD check passed — every linked issue's definition of done is fully checked.

…the ADR to 0017

The import is used only by the signing and verifying code, so a default
build — every CI job except the new feature matrix — failed
`-D warnings` on `unused_imports`. Gated to `record-attestation`, with
the one ungated doc link that named the type rewritten as an explicit
path so it still resolves with the import absent.

The local check that reported this clean was `rg -c '^(error|warning)'`
over cargo's output. Cargo colourises when it thinks it is talking to a
terminal, so the escape sits before the word and `^error` matches
nothing — a filter that cannot see the errors, reported as silence. The
loop also mis-quoted `--features X` as one argument, so three of the
five combinations never ran at all. Re-verified by exit code.

ADR 0012 renumbered to 0017: PR #106 adds a differently-named
docs/adr/0012-*.md, and two files with different names merge cleanly
into a tree holding two ADR 0012s with nothing to catch it. Numbers are
now allocated centrally. docs/GUIDE.md's decision log gains the entry,
along with 0009, 0010 and 0011, which had been missing since they landed
— adding a row to an index while leaving it knowingly incomplete is not
a fix.

Refs #96

Signed-off-by: macanderson <mac@oxagen.sh>
The index stopped at 0008; 0009, 0010, 0011 and 0012 had each landed
without a row. Adding only 0017 would have left a table that jumps from
0008 to 0017 and still misses four decisions, so all five are in.

Refs #96

Signed-off-by: macanderson <mac@oxagen.sh>
Rust 1.98's clippy adds `chunks_exact_to_as_chunks`, warn-by-default, so
`-D warnings` turned red on every hex decoder here — including
`attest.rs`'s `from_hex`, which predates this branch. CI pins
`dtolnay/rust-toolchain@stable`, so the toolchain moved under a tree
nobody had changed; the pre-existing site is fixed here because the job
cannot go green while it stands.

`as_chunks::<2>()` is also the better shape: the length check above each
loop already rules out a remainder, and a fixed-size chunk lets the
compiler see both indexes are in bounds.

The two copies of the hex decode in the conformance suite collapse into
one `hex32` helper that also checks the length it assumes.

A note on how the earlier local run missed this: cargo replays a cached
clippy result for an unchanged crate, so a `clippy` that had passed
before the lint existed kept reporting success. /tmp/verify.sh now
touches every source first.

Refs #96

Signed-off-by: macanderson <mac@oxagen.sh>
Two doc conflicts, both in tables this branch and main each added a row
to.

docs/GUIDE.md — main's #109 added ADR 0013 plus a comment saying 0009,
0010 and 0011 were missing from the table and tracked in #129. This
branch fills that gap, so the merged table carries 0009 through 0013 and
0017, and the comment goes with the gap it described (#129).

docs/profiles/context-exchange-provider.md §9 — LF1 and LF3 are this
branch's, describing the vectors it publishes and what the suite now
checks; LF2 is main's, where #109 moved the schema $id from a GitHub-raw
URL to the protocol's own branded, family-versioned one.

Signed-off-by: macanderson <mac@oxagen.sh>
@macanderson
macanderson merged commit 7242a1e into main Aug 30, 2026
30 checks passed
@macanderson
macanderson deleted the feat/record-hash-attestation-b726a4bc branch August 30, 2026 05:00
macanderson added a commit that referenced this pull request Aug 30, 2026
clippy 1.98's `chunks_exact_to_as_chunks` fires on `decode_hex`. The
even-length check above the call already rules out a remainder, so `.0`
discards nothing, and `&[u8; 2]` indexes without the bounds check a
`&[u8]` carries.

The sibling fix in `contextgraph-types::attest` landed with #114, so only
this site remained.

Refs #160
macanderson added a commit that referenced this pull request Aug 30, 2026
clippy 1.98's `chunks_exact_to_as_chunks` fires on `decode_hex`. The
even-length check above the call already rules out a remainder, so `.0`
discards nothing, and `&[u8; 2]` indexes without the bounds check a
`&[u8]` carries.

The sibling fix in `contextgraph-types::attest` landed with #114, so only
this site remained.

Refs #160
macanderson added a commit that referenced this pull request Aug 30, 2026
* feat(conformance): check provenance attestation adversarially (F6-F9)

SPEC.md §6.5's F6-F9 shipped with their "Verified by" column pointing at
`contextgraph_types::attest` — the implementation's own unit tests. Every
other guarantee in this protocol earns its credibility from a suite with an
adversarial mode behind it, and a guarantee whose only witness is the
implementation asserting about itself is the self-attestation §11.1 exists
to rule out.

The new `attestation` check reads the wire like the §R1, §E1 and §H4 probes
do: it takes the attester keys the handshake published, recomputes each
served frame's commitment from the frame in hand, and verifies the signature
over it in the order §6.5.4 fixes — commitment first, so "the frame moved
after signing" is never reported as "the key is wrong".

`contextgraph-example-docs` signs what it serves, and grows five
`--misbehave` modes, one per forgery the constructions exist to stop:

  forge-signature       wrong key                    -> BadSignature
  lift-signature        A's signature stapled to B   -> CommitmentMismatch
  truncate-chain        a hidden `derivation` link   -> CommitmentMismatch
  swap-content          other bytes, signed frame id -> CommitmentMismatch
  malformed-attestation garbage                      -> MalformedCommitment

`lift-signature` is the one a plausible implementation really does get
wrong — sign the bare chain head and every frame citing the same source
shares a valid signature. It serves two frames from one backing file so
their chain heads and `content_digest`s are equal and only the frame id
separates their commitments; `an_attestation_lift_differs_only_in_the_frame_id`
asserts that precondition instead of trusting it, because a mode that fails
for an unrelated reason proves nothing. Deleting the identity binding from
`frame_commitment` makes the mode pass and turns `conformance-red.sh` red,
which is the evidence that the check is worth having.

`malformed-attestation` also holds F9: the frame stays served, degraded to
unattested. The probe asks the reference host that question directly rather
than trusting its own bookkeeping, because a host that dropped such frames
would hand any peer a denial-of-service primitive.

Attestations reach the verifier through two optional envelope members
(§6.5.5): `handshake_ack.attester_keys` and `frames.attestations`, detached
per F6. Both are additive within contextgraph/1 — a peer that knows nothing
about them drops them. §6.5.2 now also pins `provider_id` to the
handshake-declared `provider.name`, the only identifier both ends observe.
Issue #90 owns the fuller wire treatment (result-set Merkle roots, inclusion
proofs); this is the minimum the check cannot run without.

A provider that publishes no key and serves no attestation passes: §6.5
makes the construction mandatory and the signing optional, and
`conformance-external.sh` treats a skip as a failure.

Closes #89

* fix(contextgraph-conformance): parse hex by const-sized chunk

clippy 1.98's `chunks_exact_to_as_chunks` fires on `decode_hex`. The
even-length check above the call already rules out a remainder, so `.0`
discards nothing, and `&[u8; 2]` indexes without the bounds check a
`&[u8]` carries.

The sibling fix in `contextgraph-types::attest` landed with #114, so only
this site remained.

Refs #160

* fix(contextgraph-host): the wire FrameAttestation lives under wire::, the trust one keeps the root export
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RecordAttestation and record_hash are declared but unimplemented

1 participant