Community Git Namespaces

Why: Know Your Developer, End to End

The ecosystem’s driving use case has always been Know Your Developer — that a commit to an open-source project was made by an identified, admitted human, not an account or an agent wearing one. Until September 2026 the chain had a gap in the middle. A community could admit a member (vetting, invitation, open request), and VGI could check in CI that a commit’s signer held git.commit.sign in the community’s trust registry. But who got that right, on which repositories, and who could create, own or maintain a repository at all was still decided on the forge — in GitHub’s organization settings, by whoever happened to be an org owner — with the registry filled in by a configuration hook. The forge was the source of truth, and the forge knows nothing about personhood or membership.

Community git namespaces close that gap by inverting it. The VTC decides; the forge and CI enforce. A community binds itself to a namespace — one owner on one forge, github.com/acme or codeberg.org/acme — and from then on every right inside it is a record the community holds, granted by members to members under rules the community publishes, projected onto the forge’s role ladder by a bridge, and published to the trust registry where verify-trust reads it. A GitHub org owner can still click buttons, but the bridge sees the difference as drift and reports it; a stranger can still open a pull request, but the community can have it closed. For the Linux kernel story this is the “after admission” payoff that the vetting design always pointed at — see kernel-web-of-trust — and it shipped as part of the VTI-Eucalyptus release (coordinated-releases) across VTI, VGI and OpenVTC between 2026-09-23 and 2026-10-06.

The Model

A namespace binds the VTC to one owner on one forge. Inside it, rights are held by DIDs on forge-qualified, lowercase resources. There are five, specified in the git-ns/right/grant/0.3 Trust Task (trust-tasks):

RightOnHolder may
git.ns.adminthe namespaceeverything below, on every repository; adopt; unbind
git.repo.createthe namespacecreate a repository, becoming its owner
git.repo.owna repositorygrant and revoke own, maintain and commit.sign there; transfer; archive
git.repo.maintaina repositorymerge and triage on the forge
git.commit.signa repository or the whole namespaceauthor commits the CI check accepts

Implication is evaluated, never recorded: own implies maintain implies commit.sign, and ns.admin implies repo.create and own everywhere in its namespace — with one deliberate exception. Because verifiers ask the registry only about git.commit.sign, the implied commit right of every owner, maintainer and namespace admin is published explicitly. A namespace admin gets no role on the forge at all: ns.admin is exercised through the VTC and the bridge, and making someone an organization owner stays a community decision outside the system.

Fixed rules versus policy. Seven rules are code, not policy, and no community can waive them: containment by whole segment (a right never crosses forges, and acme does not contain acme-labs); no escalation (a grant is bounded by the granter’s own holdings); a repository keeps an owner and a namespace keeps an admin (records with an expiry, and unratified break-glass records, do not count toward these invariants); elevated rights go to current members only (a throwaway did:key cannot stand in for a second person); policy may only narrow; and separation of duties (below). After those pass, the community’s gitNamespace Rego policy runs and can only refuse — or set operational switches: whether a maintainer may grant commit.sign, whether a departed member’s grants cascade or go to review, whether role drift is merely reported or enforced, and who may open pull requests.

Rights are capabilities on the ACL entry (VTI #1930, 2026-10-03). The first cut kept a separate rights store; Eucalyptus removed it so that there is one authority model. Every right is a resource grant on its holder’s own ACL entry — git.ns.admin qualified by git-ns:<forge>/<owner>, git.repo.manage graded create / own / maintain and qualified by namespace or repository id, or the additive git.commit.sign — each carrying its own granter (delegatedBy), time, expiry, reason and any break-glass mark. The same VtcAclEntry::can question that authorizes an administrator authorizes a git action; acl/show renders the grants; a key rotation moves them with the entry. A subject that holds rights without being a member — the bridge, an external signer a policy admits — gets an entry of community role application, which can never sign in and is never a membership. This is the same role-based administration described on verifiable-trust-community, seen from the repository side.

Separation of duties and break-glass. Nobody grants themselves an elevated right (ns.admin, repo.create, own), even when their own rights would let them grant it to anyone else — refused as git-ns:selfGrantNotAllowed, and the same rule binds adoption, repo/adopt, and reseating to oneself. Since repo/create 0.3 a namespace admin’s implied create right confers no creator ownership either: they name another member as owner or are refused (openvtc #391 closed that hole from the client side). When nobody else can grant it, the actor breaks the glass (git-ns/right/break-glass/0.1): an operation-bound passkey step-up, a written justification, a right that takes effect at once and never lapses, a breakGlass flag on the record, a Critical audit row, a notice to every other namespace administrator, and a queue item in the community’s action list where another administrator ratifies or revokes it. An unratified break-glass is a real, published right, but it does not count toward the last-owner or last-admin invariants. A community in single-administrator mode — one person under several identifiers, set at install — has the self-grant rule waived on a passkey gesture, because otherwise every elevated grant would stay unratified forever.

The Actors

  • The VTC (vtc-service/src/git_ns/) holds the records, evaluates the rules and policy, runs the projector, and never talks to the forge in bridge mode. Since VTI #1841 every git-ns administrative read is a signed Trust Task; cnm git … sends them over TSP, DIDComm or HTTPS (#1778).
  • vgi-bridge is the per-community service that does talk to the forge. It has its own DID (a minted did:peer), serves one VTC, speaks TSP or DIDComm (whichever the VTC advertises first; TSP since VGI #115), receives git-ns/bridge/job documents (0.4 and 0.5), and answers with git-ns/bridge/result and git-ns/bridge/event. It can run off the VTC’s own VTA.
  • Forge adapters sit behind a forge-neutral vgi-forge trait: vgi-forge-github (the community’s own GitHub App, device-flow account linking, rulesets) and vgi-forge-forgejo (a bot user, OAuth2 + PKCE, fast-forward-only merges — Codeberg is the worked example). The bridge reports the role map it applies (roleMapReported, event 0.3): by default own → admin, maintain → maintain, commit.sign → no forge role, ns.admin → none, always. Until a bridge has reported, the VTC assumes no map and fails closed on anything that depends on it.
  • did-git-sign is the signer a developer installs as git’s gpg.ssh.program; verify-trust is the CI check (a composite GitHub/Forgejo action) that resolves the signer’s DID, checks the SSHSIG, and asks the registry (entity, authority, git.commit.sign, <forge>/<owner>/<repo>) over TSP, then DIDComm, then HTTPS. Commits carry a Signed-by-DID trailer.
  • The OpenVTC Repos panel is the member’s side (openvtc #377, #379, #384, #391, #425, #475, #477 — r on an active community): my repos with a strongest-right badge and status, create (only if the member holds the right), grant / revoke / transfer / archive (elevated changes armed and sent only on confirmation), link a GitHub or Forgejo account, resolve drift by revert or adopt, see break-glass flags in the People table, and — since 0.5.0 — manage checkouts and commit signing from inside the TUI.
  • The trust registry receives one TRQP authorization record per live right (registry/record/put, under the VTC’s own authority), with impliedBy in its context and never the granter or reason. The projector withdraws before it publishes, reconciles against what the registry holds every fifteen minutes, and the old [hooks.git-trust] grant_on_role grants keep working as a second, roleDerived source outside bound namespaces.

The Flows

Bind. A community administrator binds a namespace (git-ns/namespace/bind), in bridge mode (a [git_ns] bridges entry names the bridge DID for that forge) or manual mode (no bridge, no forge projection, rights published only). The binder’s first git.ns.admin is not a self-grant. In bridge mode the community also grants the bridge git.commit.sign on the namespace — a service grant, grantedBy the VTC itself — for one reason given below.

Link an account. A member links their forge account to their DID (git-ns/account/link, then link-status; cnm git link or the Repos panel’s l): a device code on GitHub, a URL or QR on Forgejo. One forge account links to one member, matched thereafter by forge and account id, never by login. Departure deletes the link; nobody but the holder can unlink.

Grant. An owner grants maintain or commit.sign on their repository; a namespace admin grants own or repo.create; the fixed rules run, then policy, then the right is written on the holder’s ACL entry, projected to the forge (projectRoles job — each repository’s desiredRoles carries the highest right in each linked account’s own name), and published to the registry within one projector pass (five seconds by default). Grants of own and repo.create, transfer, archive and adopt are elevated and, because a signed Trust Task has no session to step up, elevated_requires_admin = true (the default) accepts them only from a community administrator in addition to the rights model’s own entitlement — a narrowing that waits on a member-side step-up.

Drift. The bridge compares each repository with the projection and reports every difference: roleAdded, roleChanged, roleRemoved, requiredCheckMissing, protectionWeakened, bootstrapMissing. An owner or namespace admin reverts or adopts each item (git-ns/drift/resolve); adoption is evaluated exactly as a grant from the resolver, so nobody adopts an elevated right for themselves, and a missing required check is restored automatically.

The PR-open gate (VTI #1937, VGI #133, 2026-10-04). On GitHub and Forgejo anyone who can read a public repository can open a pull request, and the forge offers no way to restrict that. The bridge reports every opened or reopened pull request (pullRequestOpened, event 0.4 — number, author, draft, from-fork; never title, body or diff); the VTC reads the policy’s pr_open level — anyone (the default, so nothing changes until a community opts in), members, committers, maintainers or named roles, with per-namespace and per-repository overrides and a pr_exempt list for bots — and, when the author’s linked account does not qualify, sends a closePullRequest job (0.5) that posts the community’s message and closes it. Owners, maintainers and the bridge’s own app account are always allowed, and their reopen is an override. The documentation is emphatic that this is hygiene, not the merge gate: it fails open, and the required commit-trust check is still what keeps untrusted commits out.

Re-signing Dependabot. Automated dependency bumps arrive as commits no member signed. Rather than exempt them, the bridge rebuilds Dependabot commits with itself as committer, adds a Signed-by-DID trailer and signs them with its own DID key — deciding from signed push webhooks, not commit authorship — and pushes with --force-with-lease. That is why the bridge holds the service grant: the CI check must find its DID in the registry.

Required approvals (VGI #144, 2026-10-06). A namespace can require 0–10 reviews before merge, written into GitHub’s VGI commit trust ruleset (stale approvals dismissed, last push approved by someone else) or Forgejo branch protection. Only accounts with write access — the projected rights holders — count as approvers, and fewer approvals than configured is reported as protectionWeakened drift.

VTA-held commit signing (VTI #1957, VGI #141, openvtc #475; 2026-10-05/06). Until Eucalyptus did-git-sign exported the Ed25519 seed from the VTA on every commit. Now it sends the commit’s SHA-512 over a new Trust Task, keys/sign-sshsig/0.1; the VTA builds the SSHSIG signed data, signs with a key that never leaves it, and returns the raw signature, which the client checks against the public key and armors locally. The credential OpenVTC mints for did-git-sign is narrowed to exactly that capability (key-export only for an older VTA), so a stolen credential can sign commits but cannot take the key. Mode is auto, vta or export; the openvtc 0.5.0 Repos panel lets a member choose the forge account each repository clones and pushes with, forks when that account cannot push, and warns that the community will close PRs from an account that is not linked.

Status as of Eucalyptus

The git-ns/* family in dtgwg-trust-tasks-tf is thirty-odd task families, all draft, with right/grant and right/revoke at 0.3 (the VTC refuses 0.1 and 0.2 so no client reaches a path without the separation-of-duties rule), bridge/job at 0.5, bridge/event at 0.4 and view at 0.5. VGI is at 0.18.2; the VTC’s git_ns module landed in #1694 on 2026-09-24 and was reworked onto the ACL in #1930. A suspended administrator — one whose removal is cooling off under the two-person rule — is projected as holding nothing: registry records withdrawn, forge roles removed, pull-request rights uncounted, the rights themselves kept (#1945).

What is not built: a member-side step-up, so elevated git actions still need a community administrator; a binding credential attesting a member’s forge account (the spec says the VTC SHOULD issue one); committers get no forge role at all and namespace admins no organization role; Forgejo has no PR gate (the job answers notCapable); the configured pr_open level is not yet shown on the namespace card; and the OpenVTC Repos panel has no “request a right” flow and does not display required_approvals. An expiring ns.admin or own cannot be the record that keeps a namespace or repository from going headless; a reseat recovers one that does.

See also: verifiable-git-infrastructure, verifiable-trust-community, trust-registries, kernel-web-of-trust, peer-identity-vetting, trust-tasks, openvtc, verifiable-trust-infrastructure