Skip to content

feat(gitlawb-attest): External Attestation v1 for ref-update certs#7

Draft
achillewasque wants to merge 1 commit into
Gitlawb:mainfrom
achillewasque:feat/external-attestation-v1
Draft

feat(gitlawb-attest): External Attestation v1 for ref-update certs#7
achillewasque wants to merge 1 commit into
Gitlawb:mainfrom
achillewasque:feat/external-attestation-v1

Conversation

@achillewasque
Copy link
Copy Markdown

Adds an optional attestations field on ref-update certs plus a small crate to sign and verify them. An envelope with no attestations serializes to identical bytes as a bare cert, so nothing on the wire changes for nodes that don't opt in.

The current trust path in register hands every new agent a flat 0.05 with nothing else to anchor to. Attestations give it something to work with: SLSA or Sigstore for human-pushed code, sandbox + capability digests for agent-pushed code, in-toto for multi-step pipelines. This PR is the protocol crate only; provider implementations live elsewhere. Covenant has the first reference impl at open-covenant/covenant agent-os/crates/covenant-gitlawb/src/exec_attest.rs (covenant/exec/v1).

Protocol

Each attestation:

  • type: discriminator, e.g. covenant/exec/v1, slsa/v1.0, sigstore/dsse/v1
  • payload: opaque JSON, type-specific
  • cert_hash: SHA-256 hex of the cert body with signatures and attestations stripped, JCS-encoded per RFC 8785
  • signer: did:key:z6Mk...
  • sig: base64url-no-pad ed25519 over JCS({type, payload, cert_hash})

Registry maps type to verifier. Policy::AcceptKnown (default) lets unknown types pass without trust so adoption stays incremental. RequireAll enforces a per-repo allowlist; RejectUnknown is strict.

JCS

RFC 8785 pins object key order, number formatting, and string escaping. Without it the cert hash and attestation signatures depend on whichever JSON library the next implementor picks (BTreeMap vs IndexMap, struct field order, etc.), and interop breaks the first time a non-Rust signer shows up.

Backwards compatibility

Two tests cover the wire shape:

  • empty_envelope_serializes_as_bare_cert: an envelope with no attestations produces the same top-level keys as a bare cert
  • bare_cert_parses_as_envelope_with_no_attestations: today's bare cert JSON round-trips into the envelope

Plus tampered payloads, cross-cert replays, mid-cert countersignature changes, and unknown types under strict policy all fail in tests.

15 tests pass. cargo fmt --all -- --check clean. cargo clippy -p gitlawb-attest --all-targets -- -D warnings clean. (Workspace-level clippy fails on pre-existing lints in gl/src/init.rs unrelated to this PR.)

Not in this PR

  • Node integration: storage, GraphQL, register flow. Happy to follow up once the protocol shape settles.
  • UI.
  • Trust-score weighting policy.

Reference implementation

End-to-end demo at open-covenant/covenant on feat/gitlawb-bridge, agent-os/examples/gitlawb-attest-demo/. Generates two ed25519 keys, builds a cert, attaches a covenant/exec/v1 attestation, prints the wire envelope, verifies it through gitlawb_attest::Registry.

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