Skip to content

Dev auths network#271

Merged
bordumb merged 19 commits into
mainfrom
dev-auths-network
Jun 14, 2026
Merged

Dev auths network#271
bordumb merged 19 commits into
mainfrom
dev-auths-network

Conversation

@bordumb

@bordumb bordumb commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

No description provided.

bordumb and others added 19 commits June 13, 2026 18:12
…519N witness keys)

KERI witnesses are conventionally non-transferable identifiers: their verkey
carries the CESR code B (Ed25519N) rather than D (Ed25519). Previously
KeriPublicKey::Ed25519 was a bare [u8; 32] tuple whose is_transferable() was
hardcoded to true, and parse() rejected the B code outright — so a receipt
signed by a standard witness could not be verified even though the raw key and
Ed25519 algorithm are identical to a working D key.

Mirror the existing P-256 1AAJ/1AAI modeling on Ed25519: the variant now carries
the transferability recorded from its code (D = transferable, B = not), so
parse() accepts B, is_transferable()/cesr_prefix() are truthful, and to_qb64()
round-trips B correctly. verify_signature() is curve-only and unchanged.

Readers that built or matched the old tuple variant are updated; raw-byte
constructors route through KeriPublicKey::ed25519() (one construction path).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
auths-keri carried only an auths-internal KeyStateNotice
({version,t:"ksn",state,dt}), not the canonical KERI key-state record
({vn,i,s,p,d,f,dt,et,kt,k,nt,n,bt,b,c,ee,di}) that keripy/keriox publish
and consume — so a thin client could neither read a peer's key state nor
publish one a peer could read.

Add a typed KeyStateRecord beside the internal envelope: the KERI wire
shape, reusing the existing KeriSequence (lowercase-hex) and Threshold
(hex/clause) serializers and an `ee` establishment sub-record {s,d,br,ba}.
from_kel replays a KEL into the record (emit); into_key_state projects a
peer's record back to KeyState (ingest) and is total — a parsed record
already carries every field a KeyState needs. The `auths key-state` CLI
(alias `ksn`) wires both directions: --from-kel emits, --ingest consumes.

The emitted record is byte-identical to keripy's reference KeyStateRecord
(same field order, labels, values), so an auths key-state reads in
keripy/keriox and theirs ingests here.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…de feature

Add auths-witness-node, a feature-gated crate that orchestrates running a
hardened witness node for operators. It composes the platform's public crate
APIs (auths-witness, auths-keri, auths-verifier) and reimplements no protocol:
it owns the operation (parsed standup intent, embedded node+monitor compose
manifest over the released image, key-custody policy, health URL), not the
bytes a stranger verifies.

Extend the `auths witness` command with the operator verb set —
up/down/status/register/logs. The clap surface always compiles in (thin defs,
no heavy deps), so help and parsing are identical in every build; the handler
is feature-split: a build with --features witness-node runs the node, a lean
default build returns one actionable line pointing operators at the witness
build and pulls none of the node's dependencies. The witness-node feature is
purely additive — the default cargo tree for auths-cli does not include the
node crate, so the lean install stays lean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ode or fails honestly

`auths witness up` now performs a genuine embedded-Compose standup instead of
printing a manifest and exiting 0: it brings the node + monitor sidecar up,
waits until the node answers its health endpoint, prints that URL, and exits 0.
Crucially it FAILS HONESTLY — non-zero exit, one actionable line, nothing left
half-standing — when it cannot stand a node up, rather than claiming a success
that reality contradicts.

- auths-witness-node::standup — the runtime: stand_up()/tear_down() over a
  ContainerEngine port and a HealthCheck port (ports/adapters; success is a node
  answering, not the command returning). One-line StandupError on every failure;
  a failed bring-up tears down what started.
- auths-witness-node::engine — shipped adapters: DockerEngine (docker compose,
  failures distilled to one line) and SocketHealthCheck (dependency-free raw
  socket GET — all it needs is whether the node answers 2xx).
- auths witness up: calls the real standup; adds --image (pin a released tag /
  run an air-gapped image). down tears down the per-port project; status reports
  healthy only if a node actually answers; down gains --port.

The witness-node feature stays additive (default cargo tree pulls no node crate);
the node crate composes the platform crates and reimplements no protocol; the
standup manifest declares a released image, never a source build.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`auths witness up` now stands up a real witness node that answers its
health URL, instead of failing on an unobtainable image and an
un-bootable manifest. Three fixes:

- deploy/witness Dockerfile: resolve the static musl target from the
  build's TARGETARCH (amd64/arm64) and add it to the toolchain AFTER the
  source copy, so the toolchain that rust-toolchain.toml selects has a
  musl std. The released witness image previously could not build at all
  (E0463 on a different toolchain instance; x86_64 hardcoded on arm64).

- auths-witness-node: the embedded standup manifest dropped a
  transparency-log monitor sidecar that was the wrong daemon for a
  single-node standup and pinned an unshippable image — it blocked the
  whole `compose up`. The manifest is now witness-only and boots healthy.

- auths-witness-node: `up` mints the node's stable signing identity at
  first boot — a 32-byte OS-CSPRNG seed pinned beside the manifest and
  injected into the node, never a key file baked into the image. Re-run
  reuses the existing identity.

The node still composes the platform's public crates and reimplements no
protocol; the witness-node feature stays additive (rand/hex are pulled
only with the feature). cargo build/clippy clean (-D warnings), 15 unit
tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A witness receipt is only corroboration if a third party who does not
trust the node can check it alone — on a clean machine, with no network
and no registry. The witness's published did:key identity embeds its
verification key, so {receipt, signature, identity} is self-contained.

- auths-verifier: verify_receipt_offline recovers the witness key from
  the published did:key and checks the signature over the canonical
  receipt bytes, returning a parsed Verified / SignatureFailed /
  UnreadableIdentity verdict (Verified is the only success arm).
- auths-witness-node: ReceiptBundle composes that surface — it
  reimplements no protocol.
- auths-cli: auths witness verify-receipt --receipt <file> exposes it
  (handler feature-gated; the lean default returns the install hint).

A tampered or foreign receipt fails closed with a distinct reason. The
default build pulls no node crate; clippy clean (-D warnings).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The witness server retained receipts and first-seen SAIDs but discarded the
event bodies, so it could not recover an identity's current key-state (keys,
thresholds, next-commitment) to serve a thin client. Retain each verified
event body (a new auths-core witness storage events table, first-seen-wins)
and serve the current key-state at a stable endpoint, GET
/witness/{prefix}/key-state, as a KERI-conformant KeyStateRecord
({vn,i,s,p,d,f,dt,et,kt,k,nt,n,bt,b,c,ee,di}) — built only via the trust
kernel's own auths_keri::KeyStateRecord::from_kel after a TrustedKel replay,
never a hand-rolled serializer. The endpoint 404s when the witness has
corroborated no events for the prefix (it cannot notice a key-state it never
saw).

Add KeyStateRecord::sequence()/check_not_stale(last_seen) (returning the
existing KsnError::Stale) and an auths key-state --ingest --reject-stale-below
<hex> flag that composes it, so a verifier already holding a newer state fails
closed on a rewind to an older notice, with a distinct reason.

The served record reconstructs byte-for-byte inside the keripy reference
implementation, and a keripy-published notice ingests on the node — the wire
shape is bidirectionally interoperable, on the running node.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tation)

A witness operator vouches for the network; the operator must in turn be
vouchable. A running witness node now exposes a signed version+digest build
attestation and `auths witness status` verifies it — a forged attestation
(validly signed, but over a different binary) is rejected.

- auths-core witness server: the node measures its own running binary
  (sha256 of /proc/self/exe at startup) and serves a BuildProof at GET /build
  pairing that self-measurement with the signed `auths artifact sign`
  attestation. 404 when no attestation was configured — a node that cannot
  prove its binary says so plainly; the server interprets nothing.
- auths-verifier: verify_build_attestation_offline — a two-leg, fail-closed
  verdict: the signature verifies against the key the self-describing did:key
  issuer embeds, AND the attested digest equals the running digest. A valid
  signature over the wrong binary lands on DigestMismatch, never Verified.
- auths-witness-node: BuildAttestation::verify -> NodeBuildVerdict composes the
  verifier; a SocketHttpFetch port reads /build. `auths witness status` renders
  the verdict and fails closed on a forged or absent build. `auths witness up
  --build-attestation` mounts the released image's attestation into standup.
- auths-witness binary: reads AUTHS_WITNESS_BUILD_ATTESTATION at boot and
  attaches the self-measured proof.

The witness-node feature stays additive (no core crate depends on the node
crate; the default build pulls none of it). No protocol is hand-rolled — the
node composes the platform's public verifier.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A node operator must never need the protocol's wire/ceremony vocabulary (key
event logs, key-state notices, self-addressing identifiers, the CESR wire,
signing thresholds, and the like) to stand a witness up, check on it, register
it, read its logs, or tear it down. That rule was previously three divergent,
hand-maintained jargon lists scattered across the crate's tests — each a partial
copy free to drift from the surface it guarded.

Lift it into one place: a canonical PROTOCOL_VOCABULARY denylist and a
whole-word, case-insensitive scan_for_protocol_vocabulary in a new vocabulary
module (re-exported from the crate root). Whole-word matching keeps benign
operator strings that merely contain the letters — "prefixed", "did:key:...",
"received" — from being false positives. The crate's own happy-path tests
(health URL, build verdict summary) now consume the canonical scanner; their
inline term arrays are deleted.

The list covers the full kernel vocabulary, not the earlier six-term subset
(adds acdc/tel/verkey/prefix/threshold and more). No operator-facing output
changed; the rule keeping it vocabulary-free is now a single enforced contract.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…keripy

A KERI inception has two prefix kinds. A self-addressing AID derives its
prefix from the event SAID (i == d), so both d and i are blanked before
hashing. A basic AID uses the controlling public key itself as the prefix
(i == k[0], e.g. a D-coded Ed25519 verkey); it is NOT self-addressing, so
only d is blanked — i stays present in the hashed bytes.

compute_said_with_protocol blanked i for EVERY icp/dip/vcp unconditionally,
keying only on event type. So for a keripy basic-prefix inception it returned
the self-addressing SAID — a confidently-wrong answer (EOoC9Auw… instead of
keripy's EAAD4cS7…), silently, rather than matching or rejecting.

Fix: blank i only when it is genuinely self-addressing — classify the i value
by its CESR derivation code (parse, don't validate) rather than by event type.
An empty i (the emit path, before finalize fills i = d), the SAID placeholder,
or a digest prefix (E…) is self-addressing; a key prefix (a verkey) is basic
and is kept during hashing, exactly as keripy 1.3.4 does. This mirrors the
discriminator finalize_icp_event already uses to decide whether to set i = d.

auths still emits only self-addressing AIDs (empty i → blanked → i = d
unchanged); this is the consume side reading the broader ecosystem's events
correctly: write one style, read all. Closes interop gap IOP-L1d.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a did:webs surface so an auths AID resolves under a standard
did:webs/DID-core resolver without the resolver speaking KERI.

auths-keri gains a did_webs module: DidWebsDocument::from_key_state
projects a replayed key-state into the ToIP did:webs reference
didDocument ({id, verificationMethod, service, alsoKnownAs}). The KEL
stays the source of truth — the document is derived, never authored.
Each current signing key becomes one JsonWebKey verification method,
built only from a decoded KeriPublicKey (parse, don't validate); the
fragment is the key's own CESR value (#DAAB…) so it self-identifies
across rotation. PublicKeyJwk projects a key into its curve-correct
JWK: Ed25519 → OKP (x), P-256 → EC (x/y from decompressing the SEC1
point). The JsonWebKey x coordinate is base64url(raw verkey) — byte-
identical to the reference resolver's generate_json_web_key_vm and to
keripy's urlsafe_b64encode(Verfer.raw).

The auths did-webs --from-kel <KEL> --domain <domain> CLI is a thin
adapter over the model (mirroring auths key-state); the crypto/wire
definition lives in auths-keri, never re-implemented in the CLI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ctions

Add an OOBI surface so an auths AID can discover, and be discovered by, KERI
peers — the bootstrap every live KERI exchange (witnessing, credential
presentation, key-state resolution) starts from.

auths-keri::oobi (the I/O-free core, parse-don't-validate):
  - Oobi: a parsed OOBI URL (<scheme>://<authority>/oobi/<cid>/<role>[/<eid>]),
    keripy's OOBI_RE grammar; every component validated at the boundary.
  - Role: a total enum over keripy's kering.Roles vocabulary.
  - LocSchemeReply / EndRoleReply: SAID-and-version-correct `rpy` records
    ({v,t:"rpy",d,dt,r,a}) byte-exact with keripy 1.3.4's Hab.reply
    (/loc/scheme {eid,scheme,url} and /end/role/add {cid,role,eid}).
  - OobiEndpoint::for_controller: the serve side — derive an AID's OOBI URL and
    its rpy reply stream from a replayed key-state.
  - ingest_oobi_stream: the resolve side — replay a fetched KEL into a verified
    KeyState, bound to the cid the OOBI claimed (a mismatched KEL is rejected,
    never silently substituted).

auths-cli `auths oobi`:
  - resolve --url [--from-file]: parse a peer OOBI, fetch its bytes (HTTP behind
    a port, or an already-fetched stream), replay + print the key-state.
  - endpoint --from-kel --authority [--url]: emit the OOBI URL + rpy reply stream
    a resolving peer fetches.

Cross-verified both directions against keripy 1.3.4: auths resolves a
keripy-produced KEL, and auths's served /loc/scheme + /end/role/add replies
(SAIDs included) are byte-identical to keripy's Hab.makeLocScheme / Hab.reply.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the Issuance & Presentation EXchange (IPEX) handshake — KERI's
standard peer-to-peer way two controllers hand over an ACDC credential,
instead of each inventing a bespoke presentation wire.

- auths-keri::ipex: typed `IpexGrant` / `IpexAdmit` `exn` records. A grant
  embeds a saidified ACDC in its `e` block addressed to the recipient; an
  admit references the grant's SAID as its prior. Both saidify byte-exact
  with keripy 1.3.4 (`keri.vc.protocoling` over `exchanging.exchange`):
  field order `{v,t,d,i,rp,p,dt,r,q,a,e}`, the top-level `d` over the whole
  record, the grant's `e.d` section SAID over `{acdc,d}`. Parse is total —
  wrong route, mismatched record SAID, or a tampered embedded ACDC are all
  rejected at the boundary.
- auths ipex grant/admit: a thin file-based CLI adapter over the module
  (mirrors `auths oobi` / `auths did-webs`); the transport/signer stay out
  of the wire logic.

Cross-verified: keripy 1.3.4 parses an auths-produced grant and recomputes
the identical `exn` SAID, and the grant/admit bytes match keripy's own
`ipexGrantExn` / `ipexAdmitExn` output for the same inputs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…xporter)

A pairing session runs inside a TLS connection, but the AEAD-sealed
SecureEnvelope proofs it carries were transport-agnostic: a proof captured
on one TLS connection could be replayed onto a different one (relay/MITM).

Bind every proof to its channel via the RFC 9266 `tls-exporter` keying
material (RFC 5705):

- ChannelBinding: a parsed 32-byte exporter (label EXPORTER-Channel-Binding,
  absent context), parse-don't-validate, constant-time eq, redacted Debug.
- EnvelopeSession::new now requires a ChannelBinding (no unbound constructor);
  the exporter is folded into the envelope-key HKDF info AND the AAD. A proof
  sealed on channel A and opened on a session bound to channel B is rejected
  with ChannelBindingMismatch; a forged binding label still fails the AEAD.
- ChannelBindingProvider port, adapted over rustls in the pairing daemon
  (rustls_channel_binding / RustlsChannelBinding). A TLS 1.3 loopback test
  proves the exporter is per-session (client==server, differs across sessions).

The SecureEnvelope wire format changed, so the cross-impl KAT vectors are
regenerated to v2 with a channel_binding_hex input.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add `auths tls-cert` (issue/verify) and the `auths-keri::tls_cert` module
(feature `tls-cert`): an X.509 leaf whose trust roots in a KERI key event
log rather than a CA. The leaf carries a `did:keri:<aid>` URI SAN (the
SPIFFE X.509-SVID identity-in-SAN pattern) plus a non-critical
AuthsKeriBinding extension (OID 1.3.6.1.4.1.59999.1.1) holding the AID's
replayed key-state. A stock TLS stack (rustls/OpenSSL/Go crypto/tls)
completes a handshake with the leaf; an AID-aware verifier re-derives the
trust by replaying the KEL, so an auths identity deploys through every load
balancer, mesh, and client that already speaks TLS — augment, not replace.

- `AuthsKeriBinding` is parse-don't-validate: built only from a resolved
  KeyState (undecodable key → error at the boundary), total over its JSON.
- The leaf uses a fresh ephemeral TLS keypair, so the AID's long-term
  signing key never goes on the wire.
- `verify_binds_to_key_state` rejects a leaf whose embedded AID / current
  keys / KEL tip / SAN diverge from a held KEL's replay; a plain cert with
  no extension is MissingBinding and cannot masquerade as KEL-rooted.
- CLI is a thin adapter over the crate (the did-webs/oobi shape); the KEL
  replay and CESR key-decode remain the single sources of truth. rcgen /
  x509-parser / yasna are gated behind `tls-cert`, off by default.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A KEL-rooted leaf already carries a did:keri:<aid> URI subjectAltName (the
SPIFFE X.509-SVID identity-in-SAN shape), but the only way to learn which
identity a cert claimed was `tls-cert verify --from-kel`, which requires
already holding the AID's KEL. Add the discovery half: read the identity out
of the SAN alone, before the KEL is in hand.

- auths-keri::tls_cert::extract_aid_from_san reads the did:keri URI from the
  cert SAN (reusing extract_did_keri_san), strips the scheme, and parses the
  AID into a validated Prefix. A malformed identifier is InvalidSanAid and a
  leaf with no did:keri SAN is NoSanIdentity — typed errors at the boundary,
  never a raw string, so a plain self-signed cert can't masquerade as carrying
  an auths identity.
- `auths tls-cert identity --cert <CERT.pem>` is a thin CLI adapter that prints
  the did:keri AID a leaf claims: the X.509-SVID "who is this peer?" read,
  offline, no KEL required. It composes with `verify --from-kel` (resolve the
  AID, then replay its KEL to root trust in the log).

Tests: AID reads out of an issued cert's SAN without the KEL; a plain cert is
NoSanIdentity; a malformed did:keri SAN AID is rejected at the parse boundary.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ects forged/revoked/stripped leaves

A KEL-rooted leaf previously only bound the AID's replayed key-state; on its
own that is not unforgeable — anyone replaying a public KEL could project the
same key-state into a leaf minted over their own TLS key, and the verifier
accepted it. The leaf now carries a TlsKeyAuthorization: a KERI signature, by
one of the AID's current signing keys, over the leaf's SubjectPublicKeyInfo DER.
Only the AID's controller can produce it.

- TlsKeyAuthorization (parse-don't-validate) rides inside AuthsKeriBinding; both
  sides re-derive the same SPKI DER (issuer from the keypair, verifier from the
  parsed cert) so there is one source of truth for what the AID signed.
- TlsKeyAuthorizer port: the core never imports a key store; an adapter supplies
  the signature. issue_authorized_kel_rooted_cert signs the ephemeral TLS key's
  SPKI and refuses at issuance if the signing key doesn't match the named
  current key.
- verify_authorized_against_key_state is the adversarial verifier: on top of the
  chain-to-the-log check it requires the authorization and checks it against the
  replayed current keys. Distinct typed rejections — Unauthorized (forged),
  MissingAuthorization (stripped), MissingBinding (plain), BindingMismatch
  (revoked/rotated), SanMismatch (spoof).
- CLI: `tls-cert issue --sign-key <AID-KEY.pem>` mints the authorized leaf;
  `tls-cert verify` is the adversarial verifier (no back-compat). 11 new tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
QUIC runs the same TLS 1.3 handshake inside its CRYPTO frames, so a KERI
identity composes with QUIC — and therefore HTTP/3 — through exactly the
two mechanisms it already composes with TLS over TCP: the KEL-rooted leaf
the server presents (re-rooted by replaying the KEL, trust in the log not
a CA) and the per-connection channel binding both endpoints export from
the connection's TLS 1.3 secrets (RFC 5705; both ends agree, two
connections differ → anti-relay).

A new `quic` feature on auths-keri adds the QUIC adapter for those two
mechanisms (quic_transport): server/client configs built from the
KEL-rooted leaf, the quinn::Connection keying-material exporter, and a
real loopback driver. `auths tls-cert quic --from-kel` is the thin CLI
adapter — it serves the leaf over a loopback QUIC endpoint, completes the
handshake (ALPN h3), re-roots the served leaf in the replayed KEL, and
proves both endpoints derive the same channel binding.

Quinn's public exporter API has no RFC 9266 "absent context" form, so the
two auths QUIC endpoints agree on the registered EXPORTER-Channel-Binding
label plus an explicit auths-quic context; the per-connection anti-relay
property holds regardless, and the distinct context keeps a QUIC and a TCP
binding for the same secrets from colliding.

quic is off by default — the core KERI types carry no QUIC dependency.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add tests/conformance — a live cross-implementation conformance suite
that drives the real auths CLI against the keripy 1.3.4 reference for
every KERI interop surface and fails on any byte/structural divergence:

  - key-state (ksn) emit + ingest
  - did:webs documents (Ed25519 + P-256 JWK)
  - OOBI serve (loc-scheme / end-role replies, SAIDs included)
  - IPEX grant / admit envelopes (top SAID + embeds-SAID)

Each surface asserts auths output == keripy oracle (computed live in
oracle.py) AND == a frozen golden vector, with a provenance MANIFEST
(sha256 + gen_command per vector). Deterministic fixtures; every CLI
invocation uses an isolated --repo tmpdir, never ~/.auths.

Wire it as a PR gate: new `conformance` job in ci.yml builds the CLI,
installs the pinned keri==1.3.4 oracle via uv, and runs the suite on
every pull request to main.
@vercel

vercel Bot commented Jun 14, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
auths Ready Ready Preview, Comment Jun 14, 2026 2:24pm

@github-actions

Copy link
Copy Markdown

Auths Commit Verification

Commit Status Details
f4db1e6a ❌ Failed Commit carries no Auths-Id/Auths-Device trailer. The prepare-commit-msg hook installed by auths init adds these on every commit — if this repo sets its own core.hooksPath (e.g. husky), the hook is bypassed; run auths doctor to check. Backfill existing commits with auths sign <ref> (rewrites the commit).
66b2bc12 ❌ Failed Commit carries no Auths-Id/Auths-Device trailer. The prepare-commit-msg hook installed by auths init adds these on every commit — if this repo sets its own core.hooksPath (e.g. husky), the hook is bypassed; run auths doctor to check. Backfill existing commits with auths sign <ref> (rewrites the commit).
0240249a ❌ Failed Commit carries no Auths-Id/Auths-Device trailer. The prepare-commit-msg hook installed by auths init adds these on every commit — if this repo sets its own core.hooksPath (e.g. husky), the hook is bypassed; run auths doctor to check. Backfill existing commits with auths sign <ref> (rewrites the commit).
9bb70cc0 ❌ Failed Commit carries no Auths-Id/Auths-Device trailer. The prepare-commit-msg hook installed by auths init adds these on every commit — if this repo sets its own core.hooksPath (e.g. husky), the hook is bypassed; run auths doctor to check. Backfill existing commits with auths sign <ref> (rewrites the commit).
d9e40afa ❌ Failed Commit carries no Auths-Id/Auths-Device trailer. The prepare-commit-msg hook installed by auths init adds these on every commit — if this repo sets its own core.hooksPath (e.g. husky), the hook is bypassed; run auths doctor to check. Backfill existing commits with auths sign <ref> (rewrites the commit).
8cfadc80 ❌ Failed Commit carries no Auths-Id/Auths-Device trailer. The prepare-commit-msg hook installed by auths init adds these on every commit — if this repo sets its own core.hooksPath (e.g. husky), the hook is bypassed; run auths doctor to check. Backfill existing commits with auths sign <ref> (rewrites the commit).
20900925 ❌ Failed Commit carries no Auths-Id/Auths-Device trailer. The prepare-commit-msg hook installed by auths init adds these on every commit — if this repo sets its own core.hooksPath (e.g. husky), the hook is bypassed; run auths doctor to check. Backfill existing commits with auths sign <ref> (rewrites the commit).
b3682bc6 ❌ Failed Commit carries no Auths-Id/Auths-Device trailer. The prepare-commit-msg hook installed by auths init adds these on every commit — if this repo sets its own core.hooksPath (e.g. husky), the hook is bypassed; run auths doctor to check. Backfill existing commits with auths sign <ref> (rewrites the commit).
ba473d29 ❌ Failed Commit carries no Auths-Id/Auths-Device trailer. The prepare-commit-msg hook installed by auths init adds these on every commit — if this repo sets its own core.hooksPath (e.g. husky), the hook is bypassed; run auths doctor to check. Backfill existing commits with auths sign <ref> (rewrites the commit).
0f74dcfd ❌ Failed Commit carries no Auths-Id/Auths-Device trailer. The prepare-commit-msg hook installed by auths init adds these on every commit — if this repo sets its own core.hooksPath (e.g. husky), the hook is bypassed; run auths doctor to check. Backfill existing commits with auths sign <ref> (rewrites the commit).
464ee684 ❌ Failed Commit carries no Auths-Id/Auths-Device trailer. The prepare-commit-msg hook installed by auths init adds these on every commit — if this repo sets its own core.hooksPath (e.g. husky), the hook is bypassed; run auths doctor to check. Backfill existing commits with auths sign <ref> (rewrites the commit).
aa5eeb46 ❌ Failed Commit carries no Auths-Id/Auths-Device trailer. The prepare-commit-msg hook installed by auths init adds these on every commit — if this repo sets its own core.hooksPath (e.g. husky), the hook is bypassed; run auths doctor to check. Backfill existing commits with auths sign <ref> (rewrites the commit).
13767be3 ❌ Failed Commit carries no Auths-Id/Auths-Device trailer. The prepare-commit-msg hook installed by auths init adds these on every commit — if this repo sets its own core.hooksPath (e.g. husky), the hook is bypassed; run auths doctor to check. Backfill existing commits with auths sign <ref> (rewrites the commit).
c4af2513 ❌ Failed Commit carries no Auths-Id/Auths-Device trailer. The prepare-commit-msg hook installed by auths init adds these on every commit — if this repo sets its own core.hooksPath (e.g. husky), the hook is bypassed; run auths doctor to check. Backfill existing commits with auths sign <ref> (rewrites the commit).
9e0584ae ❌ Failed Commit carries no Auths-Id/Auths-Device trailer. The prepare-commit-msg hook installed by auths init adds these on every commit — if this repo sets its own core.hooksPath (e.g. husky), the hook is bypassed; run auths doctor to check. Backfill existing commits with auths sign <ref> (rewrites the commit).
5331ab98 ❌ Failed Commit carries no Auths-Id/Auths-Device trailer. The prepare-commit-msg hook installed by auths init adds these on every commit — if this repo sets its own core.hooksPath (e.g. husky), the hook is bypassed; run auths doctor to check. Backfill existing commits with auths sign <ref> (rewrites the commit).
766e33b3 ❌ Failed Commit carries no Auths-Id/Auths-Device trailer. The prepare-commit-msg hook installed by auths init adds these on every commit — if this repo sets its own core.hooksPath (e.g. husky), the hook is bypassed; run auths doctor to check. Backfill existing commits with auths sign <ref> (rewrites the commit).
b470abaf ❌ Failed Commit carries no Auths-Id/Auths-Device trailer. The prepare-commit-msg hook installed by auths init adds these on every commit — if this repo sets its own core.hooksPath (e.g. husky), the hook is bypassed; run auths doctor to check. Backfill existing commits with auths sign <ref> (rewrites the commit).
0494b1e4 ❌ Failed Commit carries no Auths-Id/Auths-Device trailer. The prepare-commit-msg hook installed by auths init adds these on every commit — if this repo sets its own core.hooksPath (e.g. husky), the hook is bypassed; run auths doctor to check. Backfill existing commits with auths sign <ref> (rewrites the commit).

Result: ❌ 0/19 commits verified


How to fix

Commit f4db1e6a has no Auths signature (no Auths-Id/Auths-Device trailer).

1. Install auths

macOS: brew install auths
Linux: Download from releases

2. One-time setup (creates your identity and configures Git)

auths init

3. Sign this branch and push

auths sign origin/main..HEAD
git push --force-with-lease

For CI to verify the signer, commit an identity bundle:

auths id export-bundle --alias main --output .auths/ci-bundle.json --max-age-secs 31536000

Quickstart →

@bordumb bordumb merged commit 6e5e4c4 into main Jun 14, 2026
23 of 25 checks passed
@bordumb bordumb deleted the dev-auths-network branch June 14, 2026 14:34
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.

1 participant