Repository navigation
security: close #374 and #262 — KELs travel signed and in-band, or not at all - #375
Merged
Merged
Conversation
…ndle, not a mirrored ref The pre-push registry mirror silently no-oped against every real remote (git2 links no ssh/tls transport), so the published ref drifted until the release could not verify its own binaries. Instead of giving the mirror a transport, remove the distribution channel it fed: - pre-push hook: chaining only (core.hooksPath still must not eat repo hooks) - auths registry push/pull + storage push_registry/pull_registry: deleted - verify: a committed .auths/ci-bundle.json is auto-discovered, so a plain clone still verifies with no flags — the KEL rides the clone like the pin - release.yml: artifact verify runs on the committed bundle; a guard step verifies HEAD through the bundle first and fails with a regenerate hint when a key rotation staled it - auths-rp registry_sync: docs now state the file://-only transport scope Closes #374. Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1
Residuals 1+2 close by removal, residual 3 by authentication: - --oobi (HttpOobiResolver) and --remote (RemoteKelSource, resolver reconcile/rollback machinery) are deleted: both fed bare unsigned events to a structural replay, and the mirror that would have populated their remotes is gone (#374). Verify resolution is committed-bundle or local registry — never a network fetch. - The JSON verify contract (WASM + FFI, one shared layer) now requires a parallel base64 CESR attachments array per KEL slice and authenticates each via validate_signed_kel before any downstream replay: delegator standalone, subject against the delegator's seals, issuer standalone. Forged or stripped attachments refuse with the new kelUnauthenticated verdict kind (TS union + exhaustiveness guard + Go kinds updated). - WASM verifyDeviceLink takes the same attachments input and authenticates before the link check, mirroring validateKelJson. - rt-002-allow annotations rewritten to the ingestion-boundary justification; the stale --remote/--oobi residual clauses are gone; check-verify-path-completeness passes with 0 violations. - Cross-surface parity battery gains forged-subject-kel-attachment, forged-issuer-kel-attachment, and stripped-attachment cases (asserted byte-identical native vs FFI); fixtures regenerated with attachments, which the Node/Python/Go/TS binding tests consume as-is. Closes #262. Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1
Auths Commit Verification
Result: ✅ 2/2 commits verified |
Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1
Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1
Auths Commit Verification
Result: ✅ 4/4 commits verified |
bordumb
added a commit
that referenced
this pull request
Jul 18, 2026
…jectRoot on the verdict (#377) * security: close #374 by removal — the KEL travels in the committed bundle, not a mirrored ref The pre-push registry mirror silently no-oped against every real remote (git2 links no ssh/tls transport), so the published ref drifted until the release could not verify its own binaries. Instead of giving the mirror a transport, remove the distribution channel it fed: - pre-push hook: chaining only (core.hooksPath still must not eat repo hooks) - auths registry push/pull + storage push_registry/pull_registry: deleted - verify: a committed .auths/ci-bundle.json is auto-discovered, so a plain clone still verifies with no flags — the KEL rides the clone like the pin - release.yml: artifact verify runs on the committed bundle; a guard step verifies HEAD through the bundle first and fails with a regenerate hint when a key rotation staled it - auths-rp registry_sync: docs now state the file://-only transport scope Closes #374. Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1 * security: close #262 — signed KELs at every remaining untrusted boundary Residuals 1+2 close by removal, residual 3 by authentication: - --oobi (HttpOobiResolver) and --remote (RemoteKelSource, resolver reconcile/rollback machinery) are deleted: both fed bare unsigned events to a structural replay, and the mirror that would have populated their remotes is gone (#374). Verify resolution is committed-bundle or local registry — never a network fetch. - The JSON verify contract (WASM + FFI, one shared layer) now requires a parallel base64 CESR attachments array per KEL slice and authenticates each via validate_signed_kel before any downstream replay: delegator standalone, subject against the delegator's seals, issuer standalone. Forged or stripped attachments refuse with the new kelUnauthenticated verdict kind (TS union + exhaustiveness guard + Go kinds updated). - WASM verifyDeviceLink takes the same attachments input and authenticates before the link check, mirroring validateKelJson. - rt-002-allow annotations rewritten to the ingestion-boundary justification; the stale --remote/--oobi residual clauses are gone; check-verify-path-completeness passes with 0 violations. - Cross-surface parity battery gains forged-subject-kel-attachment, forged-issuer-kel-attachment, and stripped-attachment cases (asserted byte-identical native vs FFI); fixtures regenerated with attachments, which the Node/Python/Go/TS binding tests consume as-is. Closes #262. Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1 * fix: formatting Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1 * fix: format Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1 * sdk: presentation evidence export — the agent hands its verifier-ready proof to any relying party load_presentation_evidence gathers what load_presentation_inputs resolves PLUS the per-event CESR signature attachments each KEL carries in the registry, wire-shaped to the verify contract's field names. A KEL event without a stored attachment fails closed — evidence that cannot be authenticated is not exported as if it could. auths credential present --with-evidence emits { authorization, evidence } as one JSON object: the Auths-Presentation header plus everything a relying party with no registry replica splices into its verify request. Proven end-to-end against the market's agent write API (9-check e2e: valid create, replayed nonce, forged attachment, forged signature). The Node SDK's status enums gain KelUnauthenticated so the new verdict surfaces typed instead of through the Unknown forward-compat arm. Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1 * sdk: regenerate Node type declarations for the KelUnauthenticated status Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1 * fix: the two failures the mirror removal left behind — and the bundle learns device KELs The third-party no-flags clone test failed for a real reason: auths init delegates a DEVICE identity and commits are signed by it, but the exported bundle carried only the ROOT KEL, so a clone could not resolve the signer. The bundle now carries every delegated device KEL the root anchored (device_kels, per-event hex attachments). BundleTrust authenticates each one — self-certifying dip SAID, then validate_signed_kel with the just-authenticated root's seal index as delegator lookup — and the verify resolver serves any bundle KEL (root or device) by DID, prefix-bound. The pre-push hook test asserted the removed mirror behavior; it now pins the actual contract: chain to the repo hook, exit clean, and never name registry push again. Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1 * verifier: the Valid presentation verdict names the proven subject root A relying party crediting an account to a delegated agent's root must get that root from the verdict, never by parsing the dip out of unverified evidence. subject_root is the delegator AID when the subject KEL is dip-rooted (proven — the seal-checked replay validated the delegation) and the subject itself otherwise. Threaded through the JSON contract (subjectRoot), the TS union, and the Node SDK report. Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1 * fix: delegated attachments authenticate everywhere — and the schema catches up pair_kel_attachments now parses the full delegated attachment form (-A signatures ++ -G source seal) and rehydrates the seal onto dip/drt events before validation — the JSON body of a delegated event carries no seal, and the bilateral binding needs it. This is the same rehydration org_bundle already did locally; it now lives once, at the pairing seam, so bundle verification and the JSON contract authenticate device KELs instead of refusing on trailing -GAB bytes (the golden-path failure). Also: the three IdentityBundle test initializers gain device_kels, the identity-bundle-v1 schema is regenerated for it, and the Node report surfaces kelUnauthenticated's detail instead of dropping it. Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1 * fix: downstream replays use the rehydrated events the boundary authenticated authenticate_kel now returns the paired events — rehydration restored each delegated event's -G source seal from its attachment, and the structural replays after the boundary re-enforce that seal against the delegator KEL, so handing them the bare JSON events failed delegated subjects with SubjectKelInvalid. Same fix in BundleTrust's device KELs. Proven by the market e2e: a delegated device identity now presents, verifies, and its root credit lands on the verdict's subjectRoot (13 checks green). Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1 * sdk(node): delegateAgent mints a KERI-delegated agent, not an attestation link The napi surface now routes through agents::delegation::add_scoped — the agent gets its own dip-rooted KEL the parent anchors, with the requested capabilities as a delegator-anchored scope seal. The old path minted a did:key attestation agent, which no verifier in the delegated presentation flow could honor. The bundle drops attestationJson and gains agentPrefix; the TS wrapper converts expiresInDays to seconds instead of passing days where seconds were read. Proven by the market e2e: the SDK-minted agent presents with delegator evidence and its root credit lands on subjectRoot. Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1 * sdk(node): the relying party lives here — one call authenticates a presentation request presentationNonce parses an incoming Auths-Presentation header (parse-only, nothing trusted) so a relying party can burn its single-use challenge before verification. authenticatePresentation does everything else in Rust over the auths-rp wire types and the verifier's JSON contract: audience and nonce binding, the evidence-carrying request, KEL-authenticated verification, and the verdict-to-denial mapping. A relying party keeps storage and grant policy; it re-implements no wire, no base64, no assembly, no doctrine. Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1 * docs: the report is the only API — relying parties never inspect evidence Institutionalizes the subjectRoot precedent: any fact an RP acts on is a verdict field the verifier proved, and the wire lives once behind the SDK's relying-party surface. RP-side evidence parsing is a missing verdict field, to be fixed in auths-verifier, never in the consumer. Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1 * lint: bindings are a presentation layer — check-binding-boundary packages/auths-node marshals; it does not reach into auths_id/auths_core/ auths_storage primitives (the delegateAgent incident — bindings minting attestation agents long after the SDK went KERI-native — is the class this prevents). Existing reaches carry a binding-boundary-allow annotation and shrink over time; a new unannotated deep import fails CI. Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1 * sdk(node): the contract ships with the package — conformance vectors + canonical statuses conformance/ carries the exact golden vectors the Rust verifier tests against (valid + revoked presentations/credentials) and statuses.json parsed from the generated declarations, so a consumer asserts against the contract version it actually runs — the seam is tested from both sides, versioned together. Synced on build and prepublish; published in files. Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1 * sdk(node): mintChallengeNonce — the nonce shape is the contract's, not the caller's 32 random bytes in base64url via the auths-rp Nonce type, so a relying party never invents its own format and parse_b64url on the other side can never disagree with the mint. Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1 * style: rustfmt on the binding-boundary check Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1 * fix: formatting Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1 * ci: the four reds — fmt-proof boundary annotations, OsRng, regenerated declarations The binding-boundary marker now counts on the line above or below (rustfmt floats trailing comments off brace lines), and every grandfathered annotation sits on its own line above its import. mintChallengeNonce draws from OsRng explicitly (rand::random may delegate to thread_rng — the disallowed-methods rule). The regenerated Node declarations (agentPrefix) ride along for the docs build. Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1 * style: rustfmt pass on the boundary check Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1 * sdk(node): the hand-maintained native shadow catches up with the KERI delegateAgent lib/native.ts redeclares the napi surface for the typed wrapper; its NapiDelegatedAgentBundle still had the attestation-era shape, so typedoc failed on agentPrefix. Shadow now matches the generated declarations (agentPrefix in, attestationJson out, expiry in seconds). typedoc: 0 errors. Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1
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.
What this closes
#374 (registry mirror transportless) — closed by removal. The pre-push mirror silently no-oped against every real remote (git2 links no ssh/tls), so the published
refs/auths/registrydrifted until releases could not verify their own binaries (v0.1.4, v0.1.5). Instead of giving the mirror a transport, this removes the distribution channel it fed and replaces it with the committed identity bundle:release.ymlverifies through.auths/ci-bundle.json(no refs seed step), with a staleness guard: HEAD is verified through the bundle first, so a key rotation that stales the bundle fails fast with a regenerate hint instead of at the last release step.auths verifyauto-discovers a committed.auths/ci-bundle.json— a plain clone still verifies with no flags; the KEL rides the clone exactly like the.auths/rootspin.auths registry push/pull,push_registry/pull_registry, and the hook's mirror block are deleted (the hook remains, chaining only —core.hooksPathmust not eat repo-local hooks).auths-rpGitRegistrySyncdocs now state the deliberate file://-only transport scope instead of advertising ssh/https that could never work.#262 (unsigned KELs at untrusted boundaries) — residuals 1+2 closed by removal, residual 3 by authentication.
--oobi/--remoteare deleted. Both fed bare unsigned events into structural replay (RT-002's exact shape), and with the mirror gone their feeds had no producer. Note the broken git2 transport was the only thing keeping--remoteunreachable; removing both sides together is what keeps that hole closed.…KelAttachmentsB64array per KEL slice and authenticates each viavalidate_signed_kelbefore any downstream replay — delegator standalone, subject against the delegator's seals, issuer standalone. Forged/stripped attachments refuse with the newkelUnauthenticatedverdict kind.verifyDeviceLinkgains the same attachments input, mirroringvalidateKelJson.rt-002-allowannotations rewritten to the ingestion-boundary justification;check-verify-path-completenesspasses (0 violations).Verified
check-command-driftclean after doc updates (CLI reference, git guides, kel-distribution architecture note).Notes for reviewers
verifyDeviceLinkhas a new parameter. The external widget (auths-verify-widget#4) must adopt the new signature when it updates.auths-rpremains the port where a network sync adapter would land if ever needed.wasm_verify_presentation_jsonis now safe to expose to stranger-supplied KELs.🤖 Generated with Claude Code