| Internet-Draft | Identity-Attributed Commits | October 2026 |
| Morrison | Expires 12 April 2027 | [Page] |
This document defines a git commit trailer grammar for
identity-attributed contributions using the ~handle identity
primitive defined in the MCP DNS discovery draft. The grammar binds sovereign actors,
automated bots, and AI instruments to specific commits via three
tier-structured trailers (Acted-By, Executed-By, Drafted-With)
and three optional cryptographic trailers (Identity-Signature,
Identity-Key-Id, Identity-Anchor). The signature is computed
with Ed25519 over the commit's patch identifier, the value
git patch-id derives from the change the commit makes, under a
fixed domain-separation prefix. It therefore attests the change
and not the resulting tree or the commit hash. A commit that
carries the same change keeps a verifying signature across rebase
onto an unrelated base, cherry-pick, and message amendment. A
merge commit carries no signature, and a squash is a new change
signed by whoever squashed it. Conformant parsers reject cross-tier category
errors (e.g., an Instrument-tier handle in an Acted-By slot) as
malformed. The mechanism is provider-neutral, depends only on
DNS and the ~handle resolution algorithm of that draft, and
requires no central authority or platform-specific verification
service.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 12 April 2027.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
Source-control workflows now produce commits whose authorship is shared between human contributors, automated bots, and AI instruments operating under varying degrees of delegation. Each of the existing mechanisms for attaching identity to a commit covers only part of this mix:¶
Git Signed-off-by [DCO]. A legal attestation of contribution
rights under the Developer Certificate of Origin. It carries no
cryptographic identity proof, no tier distinction, and no
resolution to a verifiable key. A Signed-off-by: line is
whatever the committer types.¶
Git commit signing (git commit -S). Cryptographically
binding, but the key model is provider-locked: GPG keys uploaded
to GitHub, SSH keys uploaded to GitLab, with each platform
maintaining its own key directory. There is no DNS-resolved key
path and no canonical identity-to-key mapping.¶
Sigstore / gitsign [GITSIGN]. A keyless signing path using short-lived certificates issued from OIDC identity tokens and recorded in the Rekor transparency log. The cryptography is sound, but the identity layer is bound to the operator of the OIDC provider. Migrating between providers re-roots identity. No tier structure exists for non-sovereign signers.¶
The Co-Authored-By: trailer [GH-COAUTHOR]. A plain-text
trailer that names a co-author by name and email address, and that
AI tools commonly use to name a model. The value is text. It has
no resolution path to an identity record or to a key, and nothing
in it distinguishes a person from a tool.¶
None of the above provides a provider-neutral, DNS-resolvable, tier-structured identity binding for the human/bot/AI contribution mix that has become typical of agent-augmented codebases.¶
Attribution rests on the chain of signed attestations a commit carries, which a copy of the same content does not carry ([MORRISON-IFT], Section 6.5).¶
This document defines a trailer grammar with the following goals:¶
Provider-neutral. No dependency on any specific identity provider, certificate authority, or transparency log operator.¶
DNS-resolvable. Public key material is reached through the
_alter DNS TXT [RFC1035] record of [MCPDNS], under a zone
associated with the handle as [ALTER-URI] Section 3.4 describes.¶
Tier-structured. Three distinct trailer slots correspond to three distinct identity tiers: Sovereign (humans and organisations with cryptographic agency), Bot (autonomous agents under scoped delegation), and Instrument (AI models and tool classes that lack keys). Each slot accepts only handles from its corresponding tier.¶
Cryptographically verifiable at the sovereign layer. Sovereign attribution is bound by an Ed25519 signature whose public key is reachable from DNS without prior trust establishment.¶
Category-safe against misattribution. Conformant parsers
reject cross-tier handle placement (e.g., an Instrument handle
in an Acted-By slot) as a structural grammar violation, not a
policy decision. Misattribution is detected at parse time.¶
This document specifies:¶
Multiplicity, placement, and ordering rules.¶
The Ed25519 signature algorithm over the commit's patch identifier.¶
Verifier behaviour for accepting, rejecting, and surfacing attribution states.¶
Security considerations specific to the trailer mechanism.¶
This document does NOT specify:¶
The ~handle identity primitive itself. This is defined by
[MCPDNS] and incorporated here by reference.¶
Background to the tier taxonomy beyond what Section 3 of this document states.¶
Sovereign key custody, derivation, and recovery. These are out of scope for this document; implementations are expected to apply standard hardware-backed-key custody practice as summarised in Section 9.7.¶
The IdentityLog transparency-log mechanism backing the optional
Identity-Anchor trailer.¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
A ~-prefixed identifier per [MCPDNS]. Handles are the unit
of identity addressing in this document. A handle carries no zone
of its own. Resolution queries the _alter underscore-prefixed
TXT record of the zone named by an org-scope, or of a zone
associated with the handle out of band, per [ALTER-URI]
Section 3.4, and never a zone derived from the handle's
spelling. A handle that
matches the handle-ref rule of [ALTER-URI] names the same subject
as the alter: URI formed by prefixing it with alter:; the
trailer value ~alice corresponds to alter:~alice. The trailer
grammar of Section 4.1 carries the handle without the scheme name.¶
A handle representing a human individual or formal organisation
with direct cryptographic agency. Holds its own private key.
Can sign. Examples: ~alice, ~example.com, ~example-co.net.¶
A handle representing an autonomous agent acting under scoped
delegation from a sovereign. Holds a scoped key whose authority
is bounded by the sovereign's published delegation policy. Can
counter-sign within the delegation envelope. Examples:
~example-deps.bot, ~example-merge.bot.¶
A handle representing an AI model, API endpoint, or tool class.
Does NOT hold cryptographic keys. Cannot sign. Exists as a
descriptive label only, suitable for attaching
provenance metadata to a contribution without making any
identity claim that requires cryptographic backing. Examples:
~cc-example-model-1, ~cc-example-model-2, ~cc-example-model-3.¶
The value printed in the first field of git patch-id --verbatim
[GIT-PATCH-ID] when given, on standard input, the output of
git diff-tree -p --root <commit>. It is a function of the
change the commit makes, meaning the per-file diffs including
their context lines, and not of the commit's parents, message,
author, committer, timestamps, or the rest of the tree. It is
hexadecimal digits: 40 in a SHA-1 repository and 64 in a SHA-256
repository, in the tests this document's author ran with git
2.56.0. [GIT-PATCH-ID] describes it as a sum of SHA-1 hashes of
the per-file diffs; in a SHA-256 repository git computes it with
that repository's hash. A commit with more than one parent, and a commit
whose diff is empty, produces no output and has no Patch
Identifier.¶
The constraint that a given trailer name accepts handles only from its corresponding tier. Cross-tier placement is a grammatical error, not a policy violation.¶
A consumer of commit trailers that implements the parsing, rejection, and signature-verification rules defined in Section 7.¶
The trailer grammar in Section 4 partitions handles into three tiers. This section defines the taxonomy normatively for the purposes of this specification. Downstream attribution grammars that reuse the taxonomy reference this document.¶
| Tier | Cryptographic Agency | Trailer Slot | Examples |
|---|---|---|---|
| Sovereign | Holds own key, signs |
Acted-By:
|
~alice, ~example.com, ~example-co.net
|
| Bot | Scoped delegated key |
Executed-By:
|
~example-deps.bot, ~example-merge.bot
|
| Instrument | No key, no signature |
Drafted-With:
|
~cc-example-model-1, ~cc-example-model-2, ~cc-example-model-3
|
A handle's tier is read from its lexical form, as in [ALTER-URI]
Section 3.3: a trailing .bot marks the Bot tier, a leading cc-
marks the Instrument tier, and a handle carrying neither marker is
Sovereign-tier. DNS does not settle a tier, and the envelope of
[MCPDNS] carries no tier field. Implementations MUST NOT assign a
handle a tier other than the one its lexical form gives.¶
Instrument-tier handles cannot make attestational claims. A
Drafted-With: trailer is informational
provenance metadata, not a verifiable identity binding.¶
The following ABNF [RFC5234] defines the syntax of each trailer. Implementations MUST accept exactly this grammar.¶
acted-by-trailer = "Acted-By:" SP sovereign-handle LF
executed-by-trailer = "Executed-By:" SP bot-handle LF
drafted-with-trailer = "Drafted-With:" SP instrument-handle LF
identity-signature = "Identity-Signature:" SP "ed25519:"
base64url-signature LF
identity-key-id = "Identity-Key-Id:" SP did-alter-uri LF
identity-anchor = "Identity-Anchor:" SP "identitylog://"
timestamp "Z/sth/" seq "#" commit-id LF
sovereign-handle = "~" handle-name
; MUST NOT end in ".bot" and MUST NOT
; begin with "cc-"
bot-handle = "~" handle-name
; MUST end in ".bot"
instrument-handle = "~" handle-name
; MUST begin with "cc-" and MUST NOT end in
; ".bot"; tier determined by the "cc-" prefix
; per [ALTER-URI]
handle-name = ALPHA 0*62( ALPHA / DIGIT / "-" / "." )
; letter first, per [ALTER-URI]
bot-suffix = %x2E.62.6F.74
; ".bot", matched case-sensitively
did-alter-uri = "did:alter:" sovereign-handle "#" key-id
key-id = 1*64( ALPHA / DIGIT / "-" / "_" )
base64url-signature = 86( base64url-char ) "=="
; 64-byte Ed25519 signature, base64url-encoded
base64url-char = ALPHA / DIGIT / "-" / "_"
timestamp = 4DIGIT "-" 2DIGIT "-" 2DIGIT
"T" 2DIGIT ":" 2DIGIT ":" 2DIGIT
seq = 1*DIGIT
commit-id = 40HEXDIG / 64HEXDIG
; SHA-1 or SHA-256 commit identifier
¶
The terminals ALPHA, DIGIT, HEXDIG, SP, and LF are
imported from [RFC5234]. The lines of a git commit message are
terminated by LF (%x0A), so the trailer rules end in LF and not in
CRLF. A carriage return at the end of a line is not part of the
trailer value, and a verifier MUST treat a trailer whose value ends
in one as malformed.¶
The three handle rules above match the same strings apart from the
.bot and cc- constraints, because a handle name may contain a dot (for
example ~example.com). The constraints are stated in comments in
the ABNF and are normative. A handle ending in bot-suffix is a
Bot-tier handle and is valid only in an Executed-By: slot, and a
handle not ending in it is never valid there.¶
The .bot suffix makes the tier syntactically distinguishable for
Bot trailers, and the cc- prefix does the same for Instrument
trailers, as in [ALTER-URI]. A Sovereign handle carries neither
marker, and that absence is what makes it Sovereign-tier. Cross-slot
placement is rejected by the verifier (Section 7).¶
Trailers MUST appear in the commit message footer block per the
git trailer convention [GIT-TRAILERS]. The footer block is
separated from the commit message body by exactly one blank line.
Each trailer occupies one line of the footer block in the form
Key: Value.¶
A commit message that places trailers anywhere other than the footer block (e.g., interleaved with body paragraphs) is malformed under this specification. Conformant verifiers MUST refuse to parse trailers from outside the footer block.¶
Trailers SHOULD appear in the following canonical order:¶
Verifiers MUST accept trailers in any order, but emitters SHOULD follow the canonical order to support diff-based review.¶
The following multiplicity constraints apply to a single commit:¶
Acted-By:: At most one trailer per commit. A squash is a
new change and is attributed to whoever performed it, so the
Acted-By: trailers of the commits it replaces are not carried
into it, and a commit with more than one Acted-By: trailer is
malformed.¶
Executed-By: - At most one trailer per commit. A commit
is executed by at most one bot in a single delegation context.¶
Drafted-With: - Zero or more trailers per commit.
Multi-instrument drafting (e.g., a commit drafted partly with
~cc-example-model-1 and partly with ~cc-example-model-2) is
permitted and expected. Multiple Drafted-With: trailers on a single commit
form an unordered set; order of appearance is not semantically
significant and verifiers MUST NOT attribute differential
authority to earlier-appearing entries.¶
Identity-Signature: and Identity-Key-Id: - These two
trailers MUST appear together or not at all. An
Identity-Signature: without an Identity-Key-Id: is malformed,
and vice versa. When present, they bind to the Acted-By:
trailer of the same commit, except as Section 8.4 provides for
member signatures on a mode commit. A commit with more than one parent
MUST NOT carry them (Section 5.2).¶
Identity-Anchor: - OPTIONAL in this version of the
specification. Implementations targeting Rung-3-compliant
attribution (transparency-log-anchored) MUST emit it; all
other implementations MAY omit it.¶
The signature algorithm is Ed25519 [RFC8032], which uses SHA-512 internally and produces a 64-byte signature over an arbitrary input message. Implementations MUST use Ed25519 and MUST NOT use Ed25519ph or Ed25519ctx variants.¶
The signed payload is the concatenation of a fixed prefix, an object-format octet, and the commit's Patch Identifier (Section 2.2) in binary form.¶
The prefix is the 38 octets of the ASCII string
identity-attributed-commit/patch-id/v1 followed by one NUL octet,
39 octets in all:¶
69 64 65 6e 74 69 74 79 2d 61 74 74 72 69 62 75 74 65 64 2d 63 6f 6d 6d 69 74 2f 70 61 74 63 68 2d 69 64 2f 76 31 00¶
The prefix is followed by one octet naming the object format of the repository the commit belongs to: 0x01 for SHA-1 and 0x02 for SHA-256. The Patch Identifier follows, as the hexadecimal digits git prints decoded to octets. A verifier MUST reject any identifier that is not 40 or 64 digits long. The payload is the prefix, the format octet and the identifier, 60 octets for a 40-digit identifier. The prefix separates this signature from any other use of the same key over other data, and the trailing NUL ends it so that no longer string can share it as a prefix. The format octet keeps an identifier computed in one object format from being presented as one computed in the other.¶
The Patch Identifier is obtained with:¶
git diff-tree -p --root <commit> | git patch-id --verbatim¶
taking the first field of the output. git diff-tree is a plumbing
command and does not read the diff.* configuration that changes
the output of git diff. A signer working before the commit exists
obtains the same input with git diff-index -p --cached HEAD, or
against the empty tree for a root commit, and not with git diff
--cached, which reads that configuration.¶
A commit with more than one parent has no Patch Identifier, because
git diff-tree -p prints nothing for it. A merge commit therefore
carries no Identity-Signature: and no Identity-Key-Id:, and
neither does a commit whose diff is empty. The --verbatim option
is not present in every git release (Section 9.6).¶
A signature is useful if it survives the operations that carry a change from one place in history to another. A commit hash does not, and it cannot contain a trailer that signs it. Rebase, cherry-pick, amend, and filter-branch all change the commit hash.¶
The tree hash does not either. A tree names the whole state of the repository after the commit, so it changes whenever the parent changes. A commit rebased onto a different parent has a different tree, and a squash has a tree that no contributor signed.¶
The Patch Identifier depends on what the commit changes and on the context lines around it, and on nothing else in the commit. A commit moved onto a different parent keeps the same identifier when its diff is the same, and loses it when the base has altered the lines the diff touches or the lines beside them. The signature then attests the change the sovereign made. It does not attest the state the repository was in before or after it. The costs of that choice are set out in Section 9.6.¶
Where stronger anchoring is required, the optional
Identity-Anchor: trailer binds the signature to a specific
commit-id within a transparency log entry, recovering commit-level
identity at the cost of an external dependency.¶
The signature is encoded for placement in the trailer as:¶
ed25519:<base64url-signature>¶
The base64url encoding follows [RFC4648] Section 5 (URL- and
filename-safe alphabet) without line breaks. A 64-byte Ed25519
signature encodes to 86 base64url characters plus two = padding
characters, for a total of 88 characters in the trailer value
following the ed25519: prefix.¶
Sovereign keys are derived out-of-band; their public components
are published under the sovereign's _alter DNS record per
[MCPDNS]. Key derivation, custody, and recovery procedures are
out of scope for this document. This document treats the
sovereign key as a pre-existing Ed25519 keypair whose public
component is reachable via the DNS-resolved path of Section 6.1.¶
Key rotation is supported by the Identity-Key-Id: trailer, which
identifies which key was used to sign a given commit. A
sovereign's DNS record MAY publish multiple historical keys
indexed by key-id, allowing verifiers to validate older commits
against the key that was current at the time of signing even
after the sovereign has rotated their primary signing key.¶
The sovereign handle's public key is resolved via the [MCPDNS]
_alter.<zone> DNS record mechanism, where the zone is the one
associated with the handle per [ALTER-URI] Section 3.4. Verifiers
MUST use the resolution algorithm specified in [MCPDNS] to obtain
the public key corresponding to the key-id named in the
Identity-Key-Id: trailer.¶
Verifiers MUST require DNSSEC [RFC4034] validation on the
_alter.<zone> lookup, as [MCPDNS] does. [MCPDNS] defines no
other retrieval path for the envelope. A key that cannot be
retrieved and validated this way is unresolved, and Step 3 of
Section 7 applies.¶
This document defines no record for an Instrument-tier handle,
and [MCPDNS] gives one no envelope. A verifier MUST NOT treat
anything published about an Instrument-tier handle, such as its
provider, version or capability profile, as an attestational
claim. Instrument handles cannot sign commits; a Drafted-With:
trailer says which instrument was involved, not that it
authorised the commit.¶
A conformant verifier MUST perform the following steps in order:¶
Parse all trailers from the footer block. Trailers appearing outside the footer block MUST be ignored.¶
Reject cross-slot category errors. For each trailer,
determine the handle's tier from its lexical form (Section 3).
No DNS lookup is made for this step. If any handle appears in a slot
other than its tier's slot, for example an Instrument-tier
handle in an Acted-By: slot, or a Sovereign-tier handle in
a Drafted-With: slot, the commit is malformed and the
verifier MUST reject it as a category error. The error
message SHOULD identify the offending trailer by name.¶
Verify signatures, if present. If Identity-Signature:
and Identity-Key-Id: are present, the verifier MUST:¶
a. Extract the key-id from the Identity-Key-Id: trailer.
b. Resolve the corresponding public key from the _alter record
of the zone associated with the Acted-By: handle, per
Section 6.1.
c. If the commit has more than one parent, or has no Patch
Identifier, mark it unverified.
d. Otherwise compute the commit's Patch Identifier per Section
5.2, form the signed payload, and verify the Ed25519
signature against it using the resolved public key.¶
If signature verification fails, the verifier MUST mark the
commit as unverified and MUST NOT report it as having a valid
sovereign attribution.¶
Verify the transparency anchor, if present. If
Identity-Anchor: is present, the verifier SHOULD verify the
anchor against the referenced log according to the log's own
verification protocol. Failure to verify the anchor MUST be
surfaced to the user but MUST NOT silently downgrade the
commit's verified status.¶
A conformant verifier SHOULD additionally:¶
Cache handle-to-key resolutions. DNS lookups for the same handle within a single verification pass should be performed at most once. Cache TTL SHOULD respect the DNS record TTL.¶
Distinguish attribution states in user-facing output. Verifiers SHOULD present three distinct states to users:¶
verified - Acted-By: present with a valid
Identity-Signature: resolving to the published key.¶
claimed - Acted-By: present without a signature, or
with a signature whose key cannot be resolved.¶
anonymous - no Acted-By: present.¶
Conflating these states is a security defect.¶
Because every tier is read from the handle's lexical form, Step 2 detects every cross-slot placement without DNS, including during a DNS outage. Only Step 3 depends on DNS.¶
The trailer grammar of Section 4 attributes each commit to individual
identities: one or more Sovereign actors, at most one Bot, zero or
more Instruments. A class of contributions falls outside this model.
Joint manifestations produced by a mode, a bounded, DNS-addressable
composition of Sovereigns operating under declared threshold consent,
are not authored by any single ~handle. Examples include
pair-programming commits where two Sovereigns contributed
indistinguishably, working-group decisions ratified by a quorum,
AI-majority outputs produced by a mode whose signing authority is
defined at the mode level rather than at any member's level, and
cross-organisation commits ratified jointly by two or more modes.¶
Rung 1 of this specification has no surface for such attributions.
A committer must either pick one member arbitrarily as Acted-By:,
which misattributes, or omit Acted-By: entirely, which drops the
commit to the claimed state. Rung 2 closes this gap by adding a
new trailer class, Acted-By-Mode:, whose value is a mode handle and
whose semantics are threshold-attestational rather than
individual-attestational.¶
Acted-By-Mode
The following ABNF extends Section 4.1:¶
acted-by-mode-trailer = "Acted-By-Mode:" SP mode-handle LF
mode-handle = "~" handle-name "@" org-scope
; handle-name MUST NOT end in ".bot" and
; MUST NOT begin with "cc-"
org-scope = domain-label *( "." domain-label )
domain-label = ALPHA / ( ALPHA *( ALPHA / DIGIT / "-" )
( ALPHA / DIGIT ) )
¶
A mode handle names the mode by its handle and the hosting zone of
the mode by its org-scope, the form ~<handle>@<zone> that
[ALTER-URI] gives a handle qualified by an organisational scope.
The trailer value ~release@example.org corresponds to
alter:~release@example.org. The zone is taken from org-scope
alone and never from the spelling of the handle. The @ is what
separates a mode handle from the handle rules of Section 4.1, none
of which admits it, and whether the named handle is a mode is
settled by the record of Section 8.3.¶
The semantics of Acted-By-Mode: are threshold-attestational. The
trailer asserts that the commit was authored under the compositional
consent standing of the named mode, not under the signing authority
of any single member. A commit MAY carry both an individual
Acted-By: trailer (naming the Sovereign who physically performed
the commit operation) and an Acted-By-Mode: trailer (naming the
mode under whose standing the work was performed); the combination
attests "member M committed on behalf of mode O under compositional
consent". A commit MUST be permitted to carry Acted-By-Mode: as
its sole Acted-By-* trailer class when the work is purely
mode-coupled and no single Sovereign claims primary authorship.¶
Rung 2 does not change how tiers are read, which stays lexical
(Section 3). A mode is not a fourth tier read from a spelling. It
is a handle whose record, in the _alter.<zone> TXT record of the
zone its org-scope names, declares it a mode.¶
A mode handle MUST resolve to an _alter record whose capability
declaration includes cap=mode (or an equivalent capability token
established in a later revision of [MCPDNS]). The mode record
carries, at minimum:¶
type=mode - asserts mode-tier classification.¶
threshold=<num>/<den> - declares the signing threshold required
for the mode to attest to a commit (for example, threshold=2/3
requires two of three member signatures).¶
members=<uri> - a reference to the mode's member attestation
keylist, itself a DNS-published or HTTPS-resolved JSON document
listing member Sovereign handles and their currently-valid
signing-key identifiers.¶
Verifiers MUST retrieve and DNSSEC-validate the mode record as Section 6.1 requires for Sovereign key resolution. A mode record that cannot be retrieved and validated that way is unresolved.¶
Section 7 is extended for Rung 2 as follows. For each
Acted-By-Mode: trailer on a commit, a conformant Rung 2 verifier
MUST:¶
Resolve the mode handle per Section 8.3 above.¶
Fetch the threshold-attestation metadata (threshold, members).¶
Enumerate the member signatures present on the commit, that is,
the set of Identity-Signature: trailers whose corresponding
Identity-Key-Id: binds to a Sovereign handle listed in the
mode's member keylist.¶
Verify that the count of valid member signatures satisfies the declared threshold.¶
Under the Rung 2 hard-gate profile, a commit bearing
Acted-By-Mode: whose member signature set does not satisfy the
declared threshold MUST be marked unverified. Under the Rung 2
warn-only profile (the rollout default), verification is limited to
parse-only and slot-category correctness; threshold satisfaction is
surfaced as informational but does not downgrade the commit's
verified status.¶
Acted-By-Mode: accepts mode handles ONLY. Verifiers MUST reject
a value without an org-scope, and any Bot-tier or Instrument-tier
handle, appearing in this slot as cross-slot category errors per the
rules of Section 7, Step 2. A handle whose record does not declare
it a mode fails Step 1 of Section 8.4.
The existing Acted-By: slot continues to accept Sovereign handles
ONLY, regardless of whether an Acted-By-Mode: is also present.¶
Rung 2 MUST be deployed in two sub-phases. In the parse-only
sub-phase, conformant hooks and verifiers accept and recognise the
Acted-By-Mode: trailer, enforce slot-exclusivity per Section 8.5,
and surface member signature counts informationally. They do NOT
enforce the threshold. The parse-only sub-phase is permitted before
the _alter. resolver has shipped in the referenced backend
implementation.¶
In the signature-verification sub-phase, verifiers additionally
enforce that the member signature set satisfies the mode's declared
threshold. The signature-verification sub-phase MUST NOT be enabled
before the _alter. resolver is in production and the mode record
schema of Section 8.3 is stable.¶
The warn-only profile is a permitted transitional state, not a
permanent operating mode. Once the _alter. resolver is in
production and the mode record schema of Section 8.3 is stable, a
conformant deployment MUST promote from the warn-only profile to the
hard-gate profile. A deployment MUST NOT remain on the warn-only
profile, nor stall indefinitely in the parse-only sub-phase, once
these preconditions are satisfied; continuing to accept
Acted-By-Mode: threshold claims without enforcement after that
point is non-conformant with this section.¶
If a sovereign's signing key is compromised, the sovereign rotates
the key and publishes the new key under a new key-id in their
_alter record. The previous key SHOULD remain published as a
historical record so that commits signed during its validity
period continue to verify. Sovereigns SHOULD also publish
revocation metadata distinguishing keys that were rotated for
hygiene from keys that were rotated due to compromise; verifiers
encountering a compromise-revoked key SHOULD warn the operator
that any commit signed by that key is suspect even if the
signature still validates mathematically.¶
Because Instrument handles cannot sign, the Drafted-With:
trailer is an unverified provenance claim. A malicious committer
can always paste Drafted-With: ~cc-example-model-1 into a commit they
hand-wrote. Implementations MUST treat Instrument attribution as
informational, not attestational, and MUST NOT extend trust
decisions on the basis of an Instrument trailer alone. The
Instrument tier documents; it does not attest. The sovereign signature
on Acted-By: does not repair this. It binds a real
cryptographic identity to the commit, so a false Drafted-With:
claim becomes attributable to a named signer rather than
anonymous, but it does not make that claim true and it attests
nothing about which tools were in fact used. Attributability is
the whole of what the signature contributes here, and it is an
accountability property rather than a verification one. A
deployment requiring assurance about instrument involvement MUST
obtain that assurance from a source outside this trailer grammar.
The bounds of what the signature does attest are stated in the
Limits of Sovereign Attestation subsection below.¶
This subsection bounds the verified state defined in the Verifier
Behaviour section.¶
A valid Identity-Signature: attests one proposition,
which is that at signing time the holder of the private key
published under the Identity-Key-Id: of the Acted-By: handle
asserted the signed payload of Section 5.2 over that change. It does not
attest any of the following, and a verifier MUST NOT report or
imply any of them on the strength of a valid signature alone:¶
that the sovereign authored the content, or reviewed content authored by anything else;¶
that the change was made against any particular base, or that the tree it produces is the tree the sovereign saw;¶
that any handle named in Executed-By: or Drafted-With: did
or did not participate, in any degree;¶
that a delegated actor was, at the moment it acted, operating within an authority the sovereign had granted, in scope, in force, and unrevoked;¶
that the sovereign observed the act as it occurred, or is in a position to enumerate the acts performed under its identity.¶
The signature is a point-in-time assertion over content. Delegated authority is a separate fact with its own scope, its own bounds and its own lifetime, and no property of an Ed25519 signature over a patch identifier carries it. A deployment that needs to know whether a non-sovereign actor was authorised to act for a sovereign MUST establish that from an authority record obtained out of band; specifying such a record is out of scope for this document. A valid signature in particular MUST NOT be treated as evidence that an authority existed, that its bounds were respected, or that it had not been revoked before the commit was made. The same bound applies one tier down. A Bot-tier counter-signature attests that a scoped key was used, and does not attest that the use fell within the delegation envelope the sovereign intended.¶
The absence of a trailer proves nothing either. A commit carrying Acted-By:
and no delegate or instrument trailer is not evidence that no
delegate or instrument was involved. The Negative-Attribution
Risk subsection below states the deliberate-omission case; a
further case is that a record of delegated activity may be
legitimately withheld from a particular reader without being
absent from the record itself. Verifiers MUST NOT infer absence
of delegation from absence of a named delegate.¶
Nothing in this document makes attribution complete. A signature establishes that one commit is attributable. It says nothing about whether the full record of activity conducted under that identity is visible to the person or organisation the identity belongs to; completeness of that record, where a deployment offers it, is a property of the deployment and not of this grammar. Implementations MUST NOT present signature validity to a sovereign as evidence that everything done under their identity is visible to them.¶
Attribution under this document therefore identifies who is answerable for a commit, which is recognition and not certainty ([MORRISON-IFT]). It does not establish what took place when the commit was made. Implementations SHOULD phrase user-facing output accordingly, and MUST NOT label a signed commit in terms that assert supervision, review, or authorisation.¶
A successful DNS poisoning attack against the _alter.<zone>
zone could redirect verifiers to a substitute public key under
the attacker's control. This risk is mitigated by:¶
[GIT-PATCH-ID] describes the Patch Identifier as a sum of SHA-1 hashes of the per-file diffs. SHA-1 is cryptographically weakened for collision resistance (SHAttered, 2017), and a sum of hashes is not a hash function with the properties of SHA-1 itself. This document has not analysed how hard it is to construct a second change with the same Patch Identifier, and a verifier SHOULD NOT assume more than the strength of SHA-1. An attacker who could do so could present a different change under a valid signature. The signature itself is Ed25519 and is not weakened by this. Verifiers SHOULD record the commit hash alongside the Patch Identifier in any local audit log, so that a later finding about the identifier can be checked against the history.¶
Signing the change has costs, and the following are accepted.¶
A moved commit still verifies. A commit moved onto a base the signer never saw verifies if its diff and the context around it are unchanged, because the Patch Identifier does not depend on the parent. The change may behave differently on that base. This is the bound already stated in Section 9.3, that the signature says nothing about the state the repository is in.¶
The commit message is not signed. The Patch Identifier ignores
the message, so the message and the trailers in it can be altered
without invalidating the signature. That includes the
Drafted-With: and Executed-By: trailers.¶
A rebase can break a signature. A commit rebased onto a base that alters the lines its diff touches, or the lines beside them, has a different Patch Identifier and no longer verifies. This fails closed. The same applies to a cherry-pick whose conflict resolution changes the diff.¶
Git version. The --verbatim option is needed so that
whitespace is not stripped before hashing, and it is not present in
every git release. A verifier using a git without it cannot
compute the value defined here, and a verifier MUST NOT substitute
the default or --stable mode, whose results differ for changes
that alter whitespace.¶
Object formats. The identifier was 40 hexadecimal digits in a SHA-1 repository and 64 in a SHA-256 repository in the author's tests with one git release. Other releases have not been checked, and the format octet of Section 5.2 exists so that identifiers from the two formats are never taken for one another.¶
No signature on merges or empty commits. A merge commit and a commit with an empty diff have no Patch Identifier. Their attribution rests on the parents and on whoever created them, and not on this mechanism.¶
The pre-commit hook (or analogous integration point) that invokes the signing operation is a trust-sensitive boundary: the hook runs in the unprivileged developer process and may have access to the sovereign's private key. Implementations SHOULD route signing through a privileged helper (for example, a unix domain socket exposed by a dedicated signing daemon, or a hardware authenticator using WebAuthn PRF) rather than reading the private key directly from unprivileged process memory. Direct key handling in the developer process is acceptable for prototyping but MUST NOT be relied upon in production deployments where commit attribution carries weight.¶
A committer may deliberately omit the Drafted-With: trailer to
conceal AI-instrument involvement in a contribution. This is
detectable only by out-of-band evidence and is not addressable at
the protocol layer. Where AI-disclosure obligations exist (for
example, in regulated software development contexts), they SHOULD
be enforced at the policy layer with this protocol providing the
truthful path for honest committers, not the verification path
for dishonest ones.¶
The Rung 2 extension's security value for Acted-By-Mode: rests
entirely on the member-signature threshold check defined in the
Verification subsection above. The rollout path defined in the Rung
2 Rollout subsection does not enforce that check universally. The
parse-only sub-phase, itself a MUST-mandated first phase for any
deployment whose resolver has not shipped, surfaces member signature
counts informationally without enforcing the threshold, and the
warn-only profile carries the same non-enforcement forward as the
stated rollout default. In either state, a committer holding fewer
than the declared threshold of member keys (including zero, where
local key custody permits it) can attach Acted-By-Mode: claiming
full quorum or governance ratification, and the commit's verified
status is not downgraded. Implementations MUST treat commits
carrying Acted-By-Mode: under a non-hard-gate profile as
unverified for any purpose that depends on the threshold claim. See
the Rung 2 Rollout subsection above for the MUST-level obligation to
promote a deployment out of the warn-only profile once its
preconditions are satisfied.¶
At the time of writing, IANA does not maintain a registry of git commit trailer names. If such a registry is established, this document requests registration of the following trailer names with reference to this specification:¶
Until a formal registry exists, this document recommends that implementers coordinate with the author and treat the trailer names defined here as reserved for the identity-attributed commit grammar.¶
This document depends on the did:alter: URI scheme via the
Identity-Key-Id: trailer. The alter: URI scheme is the
subject of IANA considerations in [ALTER-URI]; this document does
not separately register it.¶
The identitylog:// URI scheme used by the optional
Identity-Anchor: trailer is reserved by this document until a
registration exists. Implementations encountering identitylog:// URIs
without a registered scheme MUST treat the anchor as an opaque
reference and SHOULD NOT attempt resolution.¶
This document requests no other IANA actions.¶
The trailer grammar defined here is intended to coexist with prior commit-attribution mechanisms rather than to replace them.¶
| Mechanism | Purpose | Coexistence with this spec |
|---|---|---|
Git Signed-off-by [DCO]
|
Legal attestation of contribution rights | Orthogonal. A commit MAY carry both a Signed-off-by: and an Acted-By: trailer. They answer different questions. |
Git commit signing (git commit -S) |
Cryptographic identity via GPG/SSH key directories | Orthogonal. A commit MAY be both GPG-signed and Acted-By-signed. Verifiers handle each path independently. |
| Sigstore / gitsign [GITSIGN] | Keyless cryptographic identity via OIDC | Architecturally adjacent. Different identity provider model (OIDC + Rekor vs DNS-resolved DID + IdentityLog). May coexist. |
Co-Authored-By: [GH-COAUTHOR]
|
Plain-text co-author line | Orthogonal. Drafted-With: names an Instrument-tier handle where Co-Authored-By: names a person or a tool as text. A commit MAY carry both. |
Linux kernel Assisted-by [LINUX-AI-ASSIST]
|
Disclosure-only attribution of AI assistance in kernel contributions; legal liability remains with a human via DCO (Signed-off-by) |
Architecturally adjacent and complementary. Assisted-by: discloses AI involvement in prose-readable form; Drafted-With: names the same involvement with an Instrument ~handle. Both MAY appear on the same commit. |
Sigstore identifies signers, the DCO attests to legal rights, and
Co-Authored-By: names a co-author as text. None of them separates
a sovereign actor, a delegated bot, and a non-signing instrument in
its grammar, which is what the three trailer slots here do.¶
The author thanks colleagues at Alter Meridian Pty Ltd for the framing of identity tiers, and external reviewers who tested the tier-slot grammar and the cross-tier rejection rules. Additional contributors will be named at review time.¶
draft-morrison-identity-attributed-commits-03 (October 2026):¶
The signature is now computed over a Patch Identifier under a fixed domain-separation prefix and an object-format octet, and no longer over the tree hash. The Abstract, Section 1.3, Section 2.2, Section 5, Section 7 and Section 9 follow. The earlier statement that the tree hash is stable across rebase, cherry-pick and squash was wrong, because a tree names the whole resulting state, and it is removed.¶
A merge commit and a commit with an empty diff carry no
Identity-Signature:.¶
Removes squash aggregation of Acted-By: trailers from Section 4.4
and the aggregation-race subsection of Section 9. A squash is a
new change signed by whoever squashed it.¶
Adds the costs of the Patch-Identifier basis to Section 9.¶
ABNF: trailer lines end in LF as git commit messages do, and
handle-label is renamed handle-name. The .bot constraint on
the three handle rules is stated, and the timestamp rules no longer
refer to undefined terminals.¶
Corrects the cross-reference for key custody in Section 1.3 to Section 9.7 and the placeholder in Section 8.7 to Section 8.3.¶
Adds the missing reference for the Linux kernel Assisted-by
convention, removes a pointer to an Appendix that does not exist,
and replaces a reference to a page that no longer resolves.¶
Removes references that the text did not cite, and statements about the document's own contribution.¶
States how a handle corresponds to an alter: URI [ALTER-URI],
and removes self-description and padding from the prose. No
requirement changes.¶
Section 10.2 points at [ALTER-URI], not [MCPDNS], for the IANA
considerations of the alter: URI scheme.¶
ABNF: handle-name begins with a letter and no longer admits "_",
matching the handle grammar of [ALTER-URI].¶
An Instrument-tier handle is identified by its cc- prefix, per
[ALTER-URI], and not by DNS resolution, which cannot settle it
because [MCPDNS] gives an Instrument-tier handle no envelope. The
instrument-handle rule now requires the prefix, and the Instrument
examples carry it.¶
Instrument Metadata Resolution no longer resolves Instrument
handles through _alter, since [MCPDNS] gives an Instrument-tier
handle no envelope. The MUST NOT on attestational use is kept.¶
Every tier is read from the handle's lexical form, as in [ALTER-URI], and no longer from DNS. Step 2 of Section 7 makes no DNS lookup, and the paragraph on its DNS-unavailable fallback is removed.¶
A handle carries no zone of its own. The zone is the one an
org-scope names or one associated with the handle out of band,
as in [ALTER-URI], and never one read from the handle's spelling.¶
Removes the HTTPS .well-known fallback from Sections 6.1, 8.3
and 9. [MCPDNS] defines none and requires DNSSEC.¶
The mode handle of Section 8.2 is written ~<handle>@<zone>, and
the zone comes from its org-scope. Revision 02 read the zone from
the handle's first label, which [ALTER-URI] forbids.¶