Skip to content

Identity management: step two #130

Open

Description

@EclectickMedia

With user's becoming a thing in the codebase, I thought it would be cool to utilize git's gpg signed commit features to allow a user to "prove" their identity via public key.

Having user functionality is wonderful, but what is to stop someone from "adopting" the key of a repository maintainer? Using gpg signatures gives a git native method of verifying who said what on the timeline.

This is defeated however if there is already a system in place for handling identity verification.

Activity

  1. MichaelMure commented on May 2, 2019

    @MichaelMure
    Contributor

    Yes, that's the general idea. I haven't written much about it, but here goes the gist:

    • identities in git-bug are mutable even after creation, they use a chain of commits (each holding a full timestamped version of the identity) to be editable. The current active version is the last one in the chain, but git-bug can go back in time to check if the rules in a bug are valid with the identity state at that time.
    • each version of the identity store either zero, one or more crypto public key
    • if an identity has zero key, it is unprotected. That means that anybody can use it. This is useful for importers that don't necessarily have a public key at hand.
    • if an identity has at least one crypto key, it is protected. This means a few things:
      • to create a new version for this identity, the new commit must be signed with a key present in the precedent version, otherwise it's rejected
      • all bug editions with this identity must be signed with a crypto key valid at that time. This means that only the author can use that identity.
      • it's not possible to revert back to unprotected
      • key rotation/revocation can be implemented: simply create a new version without the old key and a new one
    • once this key system exist, we can add a permission scheme and roles (example: an admin can edit any comments but normal users can only edit their own)
    • if the keys are the same for git and git-bug, we can start to check commit signature on the normal (code) commits (!)
    • the identity subsystem could possibly be made into a standalone tool (ie, without git-bug) to embed and share public keys inside the git repo to check automatically commit signatures

    There is a few design decision to be made:

    • does that should use GPG ? How ? CLI parameters don't seems to be standardized and that can be a pain for multi-platform.
    • should we embed our own GPG implementation ?
    • does those public keys should be published in a public GPG keystore ?
    • should we import keys from GPG ?
    • if/when the webUI start to accept users from external systems, how do we create/store keys for them ?

    TODO is:

    1. implement identity edition (mostly a matter of UI/UX, core is ready)
    2. implement the storage of public keys in the identities + UI/UX to interact with
    3. design & implement the storage of the corresponding private keys somewhere in the system
    4. implement commit's signature verification for git-bug data
    5. implement basic role checks (only the author can edit his comments)
    6. implement more complex roles
  2. changed the title [-]feat: Global user/"gpg signed users"[/-] [+]Identity management: step two[/+] on May 5, 2019
  3. MichaelMure commented on Apr 24, 2020

    @MichaelMure
    Contributor

    Some of this is happening in #376

  4. MichaelMure commented on Apr 17, 2021

    @MichaelMure
    Contributor

    Still a lot to do, but git-bug's core can now store/load crypto keys and check signatures.

  5. smoyer64 commented on Jan 2, 2023

    @smoyer64
    Collaborator

    Per our digression in #963, I wanted to add a few comments related to a) actually signing commits (and as stated above identities) and impacts that this might have on the bridges.

    a. It's been a couple of years since I worked on GPG in Go but my recollection is that none of the Go libraries implements the new keybox storage for private keys. My recollection is that GPG generates all new keys into the keybox and that GPG never reads the value of the key (in normal operation - export does). Instead, GPG requires the use of the gpg-agent (which will be started if it isn't already started) and that all signing, verifying, encrypting and decrypting will be delegated to the agent. The main idea is that operations requiring a private key aren't executed by the user's GPG program but rather by an already running/trusted service. In my case (my private keys are on my Yubikeys), I believe there's a marker in the keybox that indicates the scdaemon should be used for the actual value (but this is still handled by the agent). In short (haha), this would tend to contradict the bullet above that states:

    design & implement the storage of the corresponding private keys somewhere in the system

    b. The other problem I see with the above is that when a bridge is importing an identity, it should NOT have access to the private key to sign the created (or updated) identity - and yet it needs to create the users and make sure they're not adopted by the wrong user even without those signatures. I'm also wondering about the "protection rules" above - are there any vulnerabilities to a user who manually edits their project's .git/config file or uses git plumbing commands?

  6. MichaelMure commented on Jan 2, 2023

    @MichaelMure
    Contributor

    Shot answer: I don't think those are issues, just some area in the design that need more than a naive thinking.

    Disclaimer: I never used GPG so it's a bit uncharted territory for me.

    design & implement the storage of the corresponding private keys somewhere in the system

    What I meant by that is really just "the private key is not stored in the git entity, only the public key". After that, where and how the private key is stored doesn't matter, as long as we are able to:

    1. sign git commits
    2. verify git signatures

    Note: those requirements don't imply that we have access to the private key. It would be perfectly reasonable to have those operations happening externally to git-bug, in the agent or a yubikey. I just implemented that part a bit naively for now.

    As for the identity data structure, that would mean we would store there only some kind of identifier for the private key (fingerprint, ...), to be able to search it on the user system. Once found, git-bug should make sure that it's indeed the key matching the public key.

    As for the actual code that would do that, my recollection matches yours. It might be difficult to find code that allows us to have this working easily, with great and transparent UX, and able to reach whatever the user normally use.

    b. The other problem I see with the above is that when a bridge is importing an identity, it should NOT have access to the private key to sign the created (or updated) identity - and yet it needs to create the users and make sure they're not adopted by the wrong user even without those signatures.

    My thinking about this is that a bridge would only:

    • create an identity without public key (aka unprotected), if no keys are known
    • create an identity with public key (aka protected), if some keys are known
    • update an unprotected identity, either to update identity details or add some public keys (making it protected)

    Once the identity is protected, the bridge can't touch it anymore. This could be a little weird but I suppose that's an acceptable UX if we consider that identities "live their lifes" in git-bug once imported. Possibly we can add the following for completeness:

    • update the user protected identity, as the private key is available (could be generalized as "any identity of which we have access to the private key)

    I'm also wondering about the "protection rules" above - are there any vulnerabilities to a user who manually edits their project's .git/config file or uses git plumbing commands?

    Obviously more pair of eyes on this would be great but I think it's OK? Let's try to unpack some obvious scenario:

    1. Bad actor want to adopt an unprotected identity

    This one is obvious: it's unprotected. Nothing prevent anyone to do that. Add a public key.

    1. Bad actor want to adopt a protected identity with a single commit

    In the data structure would be a single version, with one or more public key, without a git commit signature. There is no proper crypto signature, yet we have a hash of the full payload, that include the public key.

    • any update on another entity is required to be signed by one of those keys, so no abuse possible there.
      • if the "same" identity is created on another machine, it would have a different ID, and being able to fake that would require being able to do a hash collision (sha256)
      • as we have a single commit, there is no question of said identity not being fully propagated in the git remotes, it either exist fully replicated or not
    • as git-bug verify that the identity ID is the hash of the full payload, the bad actor can't tamper the payload without changing the ID, which would be dropped by other clients

    Now, once we have a matching private key, it would make sense to add a properly signed commit, just as precaution. Which brings us to ...

    1. Bad actor want to adopt a protected identity, with more commit(s) after the one introducing the public key

    In the data structure would be 0+ commit without public key, followed by 1 commit with 1+ public key, followed by 1+ commit signed by a key present and valid in the previous commit.

    Most of the previous reasoning still apply:

    • once we have a signed git commit, we have a fully signed chain. It's not possible to tamper anything without having the private key
    • bad actor can't use a leaked private key if the author has updated its identity to remove the leaked key (aka revoked it)

    The big difference is that it's now possible to have that identity non-uniformly replicated in the git remotes. There is actually two sub-scenario here:

    1. someone push incomplete changes, either only entities updates or only identity updates. This really shouldn't happen as a git push either succeed or not and we are (since recently) pushing all changes in one go. FYI: once the push is done, git-bug fully "import and accept" the identities changes first, for the simple fact that we want to have all identities around before proceeding with the rest.
    2. in a network of remotes, some remotes have one version of the identity, some other have another (older or younger). We won't consider here the case where the author makes, from a V1 identity version, a V2 on one machine and a v2' on another. That would create a network partition and ... just don't. It's a separate issue, due to identities requiring a purely linear chain of commits instead of accepting a DAG (https://github.com/MichaelMure/git-bug/blob/95911100823b5c809225d664de74ad2d64e91972/cache/identity_subcache.go#L28-L33).

    IMHO the scenario 1 is unrealistic, so onward for scenario 2:

    // TO BE CONTINUED, I'M HUNGRY

  7. github-actions commented on Jul 23, 2024

    @github-actions

    This bot triages untriaged issues and PRs according to the following rules:

    • After 90 days of inactivity, the lifecycle/stale label is applied
    • After 30 days of inactivity since lifecycle/stale was applied, the issue is closed

    To remove the stale status, you can:

    • Remove the lifecycle/stale label
    • Comment on this issue
  8. github-actions commented on Nov 1, 2024

    @github-actions

    This bot triages issues in order to help the maintainers identify what
    needs attention, according to the following lifecycle rules:

    • After 90 days of inactivity, lifecycle/stale is applied
    • After 90 days of inactivity since lifecycle/stale was applied,
      lifecycle/rotten is applied

    This bot will not automatically close stale issues.

    To remove the stale status, you can:

    • Remove the stale label from this issue
    • Comment on this issue
    • Close this issue
    • Offer to help out with triaging

    To avoid automatic lifecycle management of this issue, add
    lifecycle/frozen.

  9. trevor-vaughan commented on Jul 3, 2026

    @trevor-vaughan

    I started pulling together a new issue on this and found this conversation so chiming in with my $0.02 and what I would like in regards to identity and git-bug.

    Looking at the convo, I'm not sure why a bridge would be necessary. If you're going from the current state (anyone can be anyone) then you just need to hop in there and sign your material.

    Users should be actively encouraged to validate the keys via an active keyserver. Perhaps the keyservers you publish to can be part of the active identity.

    GPG is built on a web of trust but I don't know of many keysigning parties going on these days so it's mostly just folks publishing their own material. But, I'd propose that it's 100% better than nothing and you can always snag the one from GitHub easily.

    Summary

    I was looking at how to get git-bug to sign my material with my own keys and discovered that identities weren't really bound to any cryptographic mechanism at all. This makes identities more suggestions of an identity.

    After poking around the source, it looks like there's a wired-up GPG implementation in the system but it's not exposed anywhere except for tests.

    I'd like to be able to sign my actions in git-bug with my own hardware-bound GPG key. Ideally, this would be a list of allowed GPG keys for my identity since I use multiple for different accounts.

    I'd like git-bug to embed my public key into a GPG registry of some sort alongside my identity and have it pushed with clones so that signatures can always be validated easily.

    This would, of course, be opt-in so that the current implementation does not break.

    I don't necessarily mind the generated key path as an option, but I don't love that it's transparent. You'd need to know to back it up and it doesn't allow me to use the identity that I already have bound elsewhere across the git ecosystem.

    This also paves the way towards future encryption capabilities where an encryption key can be transparently locked/unlocked for specific GPG targets in order to create truly protected mobile bug tracking. Something like SOPS may be a good interface if the project decides to go that way.

    Proposed Capabilities

    • Honor the git configuration commit.gpgsign for the most usual workflow
    • Allow users to post-sign their own entries in case they've missed something
    • A new command binds a GPG key to the current identity something like git-bug user key add --gpg <keyid>.
      • Alternatively, this could be transparent and the key is imported upon first signing. This probably isn't ideal since it violates the property of least surprise
    • Signing shells out to gpg the same way git commit -S does, honoring gpg.program and gpg.format, and falling back to user.signingkey.
    • Verification occurs on view. There should probably be a way for users to disable this if they don't want the noise.
    • The user may add any number of keys
    • The user may remove any keys that have not yet been used for signing
    • A subcommand git-bug user key show <keyid> would print the information of the user's key so that the expiration, etc... is visible
  10. smoyer64 commented on Jul 3, 2026

    @smoyer64
    Collaborator

    The user may add any number of keys
    I like this idea in theory ... I tried it with straight Git for a while and found it was no end of pain:

    [user]
    	signingkey = 6581E2D1910FECBD!
    #	signingkey = 1B927FE8B8D21480!
    #	signingkey = E0302C3F19E25E06!
    #	signingkey = DB55A4B4F18AB0622B1CEA4DD8D464C9D22FB8E0
    #	signingkey = A0F15062A3B7AD38586422C340E3727B3F334612
    

    I don't remember the specifics. It might have been that they last key listed wins? I'll uncomment this sometime next week and get a refresher. Even with multiple keys, I only have one YubiKey at a time plugged into my laptop and there's not a good way to switch. Or better yet, to see a pop-up dialog where I can choose one. Auto-detecting which one signing keys are available would be even better!

  11. trevor-vaughan commented on Jul 3, 2026

    @trevor-vaughan

    @smoyer64 Ah! That's a good point. The user should be able to specify which key is "active". But all keys that have been used to sign something need to be present.

    It would be great if the project could just inherit whatever git is using unless specified otherwise. I just have two different configs that I point at for different purposes.

    For the first cut, just following whatever git is using seems reasonable.

  12. smoyer64 commented on Jul 4, 2026

    @smoyer64
    Collaborator

    From a straight GPG perspective, I had different GPG signing sub-keys on each of my YubiKeys and switching to the secondary or tertiary was a real pain. I've actually move to having the same signing sub-key on all my YubiKeys which avoids the "which one" problem. You still have to switch "cards" to make sure you have the right stub present.

  13. trevor-vaughan commented on Jul 4, 2026

    @trevor-vaughan

    I keep my work and personal material strictly separated, so different devices for me. It's not too bad, just have to re-pin after a swap and prod the ssh agent.

  14. MichaelMure commented on Jul 4, 2026

    @MichaelMure
    Contributor

    I actually started working again on this. Please excuse the low effort LLM summary of my plan:

      The problem with keys today                                                                                                              
      - Key changes have to be manually propagated to every repo — that's already annoying, and it becomes a real mess once key management     
      exists                                                                                                                                   
      - Identity is a linear chain, so two devices editing at the same time can split the network with no automatic resolution                 
      - ValidKeysAtTime relies on self-reported Lamport clocks — an attacker with a rotated-out key can fork from an old DAG point and inject  
      forged operations within the old key's validity window                                                                                   
                                                                                                                                               
      The core insight: decouple what keys exist from when they became effective                                                               
      - An external key log owns the key set and its history                                                                                   
      - The local identity CRDT keeps the Lamport anchoring — which key set was active at which logical time                                   
      - This preserves the critical invariant: the operation author cannot choose the key set used to verify their own operations (putting the 
      anchor in the operation pack would break this)                                                                                           
                                                                                                                                               
      If an active key is compromised, everything breaks — and that's normal                                                                   
      - This is the baseline failure mode for any static signing key system                                                                    
      - Forward-secure keys would fix it cryptographically but are incompatible with SSH agents and multi-device use, so not a realistic goal  
      here                                                                                                                                     
      - Post-rotation compromise has a partial mitigation: Lamport monotonicity makes it hard (not impossible) to forge operations with a      
      convincingly recent timestamp                                                                                                            
                                                                                                                                               
      The key log doesn't need to be DID-PLC                                                                                                   
      - The actual requirements are minimal: append-only, publicly readable, authorized writes, linear ordering, stable identity naming        
      - A plain public git repo with branch protection satisfies all of them — commit hashes are the anchors, branch protection is the         
      append-only guarantee                                                                                                                    
      - Any other git-bug instance works too                                                                                                   
                                                                                                                                               
      Surprises                                                                                                                                
      - A community git-bug deployment is automatically a valid key log backend for its users — so cross-repo identity management comes for    
      free with a shared instance, no extra infrastructure                                                                                     
      - Moving keys out of the local CRDT dissolves the linear chain requirement as a side effect — identity becomes a normal mergeable DAG,   
      and the network-split risk disappears                                                                                                    
      - Backward compat with existing identities is also free: absence of a key log URL means "use inline keys as before", no migration needed 
      - The same SSH/GPG key signs everything — git commits, operation packs, key log updates — because the backends differ only in wire       
      format, not key material; users manage zero new keys                                                                                     
      - Recording the identity namespace Lamport clock in every operation pack turns key rotations into a global consistency signal: a forged  
      operation's anachronistically low identity clock becomes detectable across the whole community, not just within one entity   
    

    TL;DR:

    • identity V3: mesh with / import keys that are already used for normal git commit signing. If not setup yet, instruct the user to do so then import into the identity. Use those keys to verify git-bug data. Change the public key format: DID style public key with multibase encoding, and optionally a full armored GPG pubkey for lossless round-trip and interop. Use the local SSH-agent as a signer to delegate complexity and not have to handle private keys. This is happening there: identity v3 WIP #1576
    • identity V4: split v3 identities in two:
      • the signed log of active pubkeys
        • this would look almost exactly like did:plc from bluesky and is a serious option. Could also be a specialized variant just for git-bug
        • this could also be another git-bug repo with V3 identities, for example an identity repo, from which normal repo feed identity from
      • how they mesh into the repo lamport clocks
        • this would remain in the repo
        • freed from the constraints of a pubkey log, that can become a proper DAG/CRDT
  15. trevor-vaughan commented on Jul 4, 2026

    @trevor-vaughan

    I'm a bit confused. Why are you hooking into SSH at all? I've used plenty of git systems where I had GPG but all actions were bound to HTTPS and SSH was disallowed. Wouldn't it be simpler to ride on top of the existing git GPG model?

    If it's GPG identities, I would stick to the regular GPG keyserver frameworks. Folks can host an internal one if they want to. I feel like reproducing the regular lifecycle inside of git-bug is going to be confusing.

    Signatures can be bound to the object. If the object has varied, then the signature is invalidated. It could be re-signed (that's fine), but an attacker with a forged key would be internally valid but not valid against a published key.

    I still don't love the magic internal key. It's reinventing a solved problem in a surprising (to me anyway) way. Users should be encouraged to sign both their regular git commits and their git-bug material and, ideally, this will be with the same mechanism and hygiene.

    I guess I'm not saying not to go through with it but that I wanted something much simpler, which was for my regular old identity that I use with git to "just work" with git-bug.

  16. trevor-vaughan commented on Jul 4, 2026

    @trevor-vaughan

    Hmm...thinking about this a bit more, how about making the entire signing/attestation capability plugin-based? I'd love to be able to also use X.509, SPIFFE, etc... depending on what/who is doing the updates.

    That would put crypto just a small hop away in the overall plugin structure.

  17. MichaelMure commented on Jul 4, 2026

    @MichaelMure
    Contributor

    Storing private keys in people system is a huge mess. There is however a somewhat standard that most developer already user: SSH agent. With that, I can ask any kind of weird setup that the user has configured, to sign an arbitrary message. I suspect that also covers GPG?

    In any case there would be a simple "signer" interface in git-bug that could be implemented to target other private key setup.

    I think we agree on the key point that git-bug should not re-invent private key handling.

  18. trevor-vaughan commented on Jul 4, 2026

    @trevor-vaughan

    git-bug should definitely not re-invent private key handling 💯

    A general interface makes sense so that folks can just do what they like. Do you think that it would be viable to make the interface be able to use whatever shell commands the user may want? This way, the signature is abstracted away from the storage medium.

    If the project were to go that way, deferring binding identities to users as an internal entity may make sense. Just like git, the signature would just move with the object that was signed and it would be up to the user to provide whatever validation mechanism they want to provide their level of comfort.

  19. trevor-vaughan commented on Jul 4, 2026

    @trevor-vaughan

    With the assistance of our robot friends, I dug through everything to see where I was getting confused. It looks like what I want in the short term is in #1576 🎉.

    I put the rest of this together to make sure that I had a good understanding of the targets and some possible cases for consideration.

    Functional Goals

    • Attribution: prove I said what I said. Verify with the GPG/SSH tooling I already have, or don't. My call. I (think) this is in identity v3 WIP #1576 and could ship to close the gap.
      • The only ask that I would have here is to be able to honor git's existing configuration so that I'm not juggling two independent thought models for the same repository.
    • Authorization: enforce "only the author edits their comment," "an admin can edit anything." This needs the key-set-at-a-point-in-time machinery, because a check each client can opt out of isn't a check.
    • Identity verification: is this key actually me? A real-world trust question that nothing can answer from inside the repo.

    Identity verification stays out of core

    "Is this key really Trevor" shouldn't live in git-bug's core, and shouldn't assume the Internet. Everyone's setup differs, and I see a real use case for git-bug in highly regulated, offline, environments. This means no keyserver, no WKD, no keybase, no sigstore public infrastructure.

    My suggestion is to make it a local shell hook, the way git delegates to gpg.program. git-bug hands it an identity and the claimed key(s); the hook checks against whatever the shop runs (internal CA, LDAP, PIV/CAC, an offline allowed_signers file, or a keyserver if you're online) and reports back. Something like the following:

    • Config lives in local/global git-bug config, never in repo data. Whoever wrote the data must not be able to set the thing that validates it.
      • It's possible that git-bug could contain a suggestion that moves with the repo but only the on-disk config gets used. Dev on a plane wants a warning and a regulated shop wants fail-closed.
    • Tri-state result: verified, rejected, couldn't-check. Rejected and unknown behave differently based on the user configuration and security model
    • Fires on first sight and on every key change. The key-change trigger is where a takeover shows up.
    • Caches the verdict (the TOFU store) so it survives offline. known_hosts for identities.

    Squatting and recovery

    While thinking about this, I was considering the following problems:

    • Squatting: if someone registers "Trevor" before I arrive, their identity is cryptographically valid, because they made the genesis. First contact is trust-on-first-use. The only fix is an external anchor (my published key, my shop's CA), checked by an external hook tailored to my environment.

    • Recovery is takeover: anyone who can recover me after I lose my key can also hand it to themselves. For a solo owner there's no separate admin to betray you, so your key is the root and recovery is your own offline backup or a Shamir split. For bigger orgs, don't give one person admin powers, split recovery across a quorum so it takes collusion. Don't add a recovery authority you don't need, and never make it a single party.

    In either case, the activity needs to be baked into an immutable log entry so that proof discovery is practical.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions