Build Report · Week 39 · the week every front door got a signature check

One door, and it asks who you are

430 changes this week, up from 302 and second only to Week 37's 463. Two stories carry it. First, every project that takes instructions now takes them through one door, and that door checks a signature. The message hub, the agent, the community service, the identity-log host and the trust registry all moved their administration onto signed, time-stamped requests that can run only once. The older, unsigned entrances were then removed, not just switched off. Second, a community can now govern its own code. It decides who may change a set of GitHub or Forgejo repositories, and a small bridge service applies that decision on the forge and reports back. Around those two: a live view into the message hub for the people who run it, a “delivered” status that now means the recipient actually collected the message, and a security week that put the agent's keys behind audited doors and made the most powerful admin role need a second admin's consent. And off the main line, on the zkp-pcs branches: working zero-knowledge proofs. An applicant can now prove that three eligible vetters vouched for them without anyone, the community included, learning which three.

📅 September 21 → September 27, 2026 · 10 projects scanned, 7 shipped
430
changes shipped across seven of the ten projects in scope
7
projects that moved their control surface onto signed requests in the same week
+2,236
new automated checks (13,793 → 16,029), like-for-like across the same eight projects
115
changes on the busiest day, Wednesday September 23
The Big Picture

Six things changed this week

Last week the stack changed the protocol it speaks. This week it changed who it listens to. Most of the six themes below come back to one idea: an instruction should carry proof of who sent it, when, and for what. It should also be possible to act on it once and no more.

🔏

One signed door, in seven projects

Administration, account changes, backups, configuration and service management now arrive as signed requests. Each one is stamped with the time it was issued, checked against the sender's published keys, and recorded so it cannot be replayed. That applies whether it arrives over the web, the secure-messaging protocol or the older encrypted-message format. Then the old doors were removed: the message hub's legacy admin protocols, the trust registry's old admin interface, the agent's unused routes, and the last legacy backup and identity-log paths.

🌳

A community that governs its own code

About 65 changes across three projects. A community can own a git namespace, a set of repositories on GitHub or Forgejo. It decides who has which rights there. A bridge service applies those rights on the forge, and the community app shows members the repositories they have access to. When someone changes a setting on the forge directly, that drift is detected, and an admin can undo it or accept it.

🔭

A control room for the message hub

The people who run a message hub had a read-only status page. They now have a console, in a terminal and in the browser wallet (as “Mediator Lens”). It shows each step of a message's life live: received, stored, relayed, collected or expired. It shows who sent a message to whom, never what it said. Limits can be changed without a restart, and a queue can be cleared only after a preview that must still be accurate when you confirm.

📬

“Delivered” now means collected

A sender used to guess that a message had been delivered because it had left the queue. That guess was wrong in both directions. A fast recipient collected messages before anyone saw them queued, so they were sent again. An expired message counted as delivered. The hub now records why each message left and will answer if asked. The agent's outbound pushes now try the secure protocol, then encrypted messaging, then plain web, stepping down only when necessary.

🛡️

The keys behind audited doors

Eighteen changes labelled as security in the trust backbone alone. Seed and key material can now be reached only through audited, scope-checked doors. A refresh token that is reused revokes the whole session. Nobody can widen their own access, and nobody can grant more than they hold. The most powerful admin role now needs another admin's consent. Across three projects, an encrypted message's claimed sender must now be the key that actually decrypted it.

🎭

Faces, made where they are needed

A person can now create the identity a community will know them by (a “face”) at the moment the community asks for it. They can retire and reinstate it, and read where it has been used. This landed in the trust backbone, the community app and the browser wallet together. A community can ask an applicant to tell it about themselves, and the applicant answers through a disclosure they control rather than a form.

Why It Matters

What this means, in plain terms

🚪

One door is easier to guard than five

Every project had grown more than one way in: a newer, signed path and an older one kept for compatibility. Having two doors means that however good the lock on the new one is, it doesn't matter while the old one is open. The work this week wasn't only about making the signed path complete. It was about removing the others, which is the part that actually closes the gap. The message hub went from “the old way is deprecated” to “the old way is switchable” to “the old way is gone” in six days.

✋

A change can't approve itself

The riskiest moment in a code-governance system is the change that edits the rules checking it. On the forges, a pull request can no longer satisfy its own check. Where a forge can't enforce that, the bridge posts the check itself, and only against the protected branch. Automated dependency updates are re-signed from where they were pushed, and only when the branch still points where it did. Merges made by the platform are trusted only when they are clean.

🔑

Two keys to open the vault

A single compromised admin account used to be able to do anything, including make itself harder to remove. Now the unrestricted admin role needs a second admin's consent, including when a co-admin is installed. An offline “break glass” override still exists for emergencies and is audited when used. No one can widen their own access. It is the same principle as a bank's two-signature cheque, applied to software.

430
changes across 7 of 10 projects. Busiest day: 115 on Wednesday September 23
+2,236
new automated checks (13,793 → 16,029), the largest weekly rise recorded here
+317k
lines added (−51.8k removed) across 1,516 files
2
forges a community can now govern, GitHub and Forgejo, each with its own adapter
★ Big outcome #1 · The week's headline

A community can now govern the code it depends on

Think of a shared allotment. The committee decides who has a plot, and a gatekeeper hands out keys to match. Previously, the committee's decisions and the padlocks on the gate were two separate systems that someone had to keep in step by hand. Now the committee's register is the list of keys. When someone leaves, their key stops working. If someone changes a padlock without asking, the committee finds out.

What changed. A community's service can now own a git namespace, a set of repositories on GitHub or Forgejo. Its membership decides who holds which rights there. A new bridge, one per community, holds the forge credentials. It applies those rights and reports back what it did, including any access it could not remove. The git-signing project gained adapters for both forges and the bridge itself, and cut four releases (0.4.14 to 0.5.1) along the way. The signature checker can now ask the trust registry whether a signer is trusted over the secure-messaging channels, with the web interface optional. Admins get a Repos screen in the community console. Members get one in the community app and a command to link their forge account to their identity.

What it guards against. A pull request can't approve itself. A namespace admin gets no forge rights just by being an admin. Elevated git rights need separation of duties, with an audited emergency override. Each member may link only one forge account, and only current members' accounts count.

🏛️
The community decides
Membership and roles in the community service decide who may do what in its repositories.
🌉
The bridge applies it
A per-community bridge sets those rights on GitHub or Forgejo and reports what it did.
✍️
Every change is checked
Commits are signed by an identity, and the check asks the trust registry. A pull request can't pass its own check.
🧭
Drift is caught
A setting changed on the forge directly shows up as drift, and an admin can undo it or accept it.
★ Big outcome #2 · The whole stack takes instructions one way

Signed, fresh, run once, over any wire, and the old doors are gone

The stack already had a standard format for instructions (“Trust Tasks”). Last week most services accepted it alongside older formats, and many accepted it without checking the signature. The message hub, for example, ran ten management tasks on proof of connection alone. This week closed that gap in seven projects at once.

✍️ Every request is signed and checked

A request that its specification says must carry a proof now has to carry one by default. It is checked against the sender's published keys, for the right purpose, and must be recent. The community app signs every request as the persona sending it. The phone approver's core library signs every request and verifies every reply.

🔁 A request runs once

A consequential request is recorded when it is accepted. The same request sent again is refused, and so is a different request reusing the same ID. Until now, a captured “update this account” could be replayed for as long as its envelope stayed valid, and to any other server sharing the same store.

📜 Answers are signed too

The message hub, the trust registry and the agent now sign their replies. A client can prove that an answer came from the service it asked, and not from something in between. The community app now checks the proof on credentials it has been issued, and authenticates replies to invitations and join requests.

🧱 The old doors are removed

The message hub's legacy admin, account and access-list protocols and three older web routes; the trust registry's old admin interface; the agent's unused routes; the last legacy backup and identity-log paths; and a whole second transport layer in the developer kit. All deleted. Service management, identity hosting and the provisioning flow are now signed requests only.

And they arrive over whatever wire is available. The same signed request now travels over the secure-messaging protocol, encrypted messaging, or plain HTTPS. The service is described once and the transport is chosen at send time. That is why the identity-log host, the trust registry and the community command-line tools could all move in the same week without each building their own plumbing.

★ Off the main line · Working zero-knowledge proofs, on the zkp-pcs branches

An applicant can prove three vetters vouched for them, without anyone learning which three

Picture a club that admits anyone three existing members will vouch for. Today each voucher signs their name, so the club and anyone who later reads its records knows exactly who vouched for whom. That is fine for a book club. It is a real risk for a community whose members could be pressured, or whose vetters could be targeted for having let someone in. What we want is for the club to be certain three different, eligible members vouched, and for nobody, the club included, to be able to say who they were.

What was built. Using the Predicate Credential System (PCS), a zero-knowledge proof scheme from published research, we built hidden-vetter admission for OpenVTC communities. A community opts in. Each vetter gets a blind credential. Each vetter's vouch carries a tag that is unique to that vetter-and-applicant pair and can't be traced back to the vetter. The applicant then proves, in one short proof, that it holds k vouches from k different eligible vetters, none of them itself. The community checks the proof using the admission rule it already had. It learns that the threshold was met, and how each vetter checked (in person or by video). It does not learn which members were involved.

It works end to end. In the reference run, ten members enrol and three of them vet one applicant. The applicant sends a single 1.3 KB proof. The community counts three distinct vetters, and none of the three tags it keeps matches any of the ten enrolled identifiers. The same proof submitted again is refused, because each submission must answer a one-time challenge from the community. The client-side PCS crate's 19 tests and the community-side verifier's 7 all pass on the branches, including adversarial cases: a token spent twice, a forged withdrawal, one member holding two key pairs, and a replayed proof.

🎟️
Vetters get a blind credential
The community issues each vetter a credential it can't later link to that vetter's vouches.
🤝
Each vouch is tagged
The same vetter twice is caught. Vouches for different applicants can't be linked together.
🧮
One proof, k vouches
About 1.3 KB, and milliseconds to check. It shows k distinct, eligible vetters without naming any of them.
🏛️
The community counts it
It applies the rule it already had, admits, and issues the membership credential. The vetters stay hidden.

What is built. Both halves, over the real wire. The community (verifiable-trust-infrastructure, 13 changes) enrols vetters, releases vouching tokens at a published rate so no vetter can vouch in bulk, issues the one-time challenge, verifies the proof, and mints the admission credentials. It serves all of this as three Trust Tasks, plus an “event mode” whose tokens expire with the event. The app (openvtc, 27 changes) reads the community's hidden-vetting settings, lets a vetter vouch from the vetting desk, tells an applicant that the community hides its vetters, and builds and submits the proof. A test fixture checks each side against the other. Operators have a runbook for standing a community up. One small change to the upstream PCS library binds every proof to its application (432 of its tests pass).

What it is not, yet. The branches are local: not pushed, not merged, and not counted in this week's 430. The last merge from main was on 23 September, so they are now behind main (openvtc by 16 changes, the trust backbone by 105), including this week's signed-door work. The PCS library is a research artefact and unaudited. The feature is opt-in per community and deliberately kept off the 5 October critical path for named vetting. The last blocker the design recorded was that the stack depended on an older version of the Trust Tasks library. That cleared on main this week, when every project moved to trust-tasks 0.22–0.24.

The Quality Delta · last report → now

How much healthier is it than a week ago?

Counted the same way as the previous ten reports, across the same eight Rust projects. Every starting figure below matches the ending figure Week 38 published. The browser wallet and the phone approver are written in TypeScript and Swift, so they add nothing to these counts. Their changes are included in the headline totals above.

Automated checks (run on every change; same eight projects)
13,793→16,029
▲ +2,236 checks (+16.2%), an eleventh consecutive rise and the largest recorded here. The trust backbone added 1,110. Git signing more than quadrupled (164 → 696), almost all around the new forge adapters and bridge
Test stand-ins (fakes that let a component be tested without the rest of the system)
865→1,103
▲ +238 (+27.5%), nearly four times last week's record (+62). 151 are in git signing, which went from none to 151 because a forge can't be tested against without a fake one
Future-proofed interfaces (surfaces that can gain a field without breaking everyone downstream)
398→555
▲ +157, driven by git signing's new crates (0 → 113), which were written future-proof from the start
Unused-code markers (lower is better, and this one moved the wrong way again)
132→154
▲ +22, worse, and by far the biggest rise yet. It is the third consecutive rise, after being named twice as next week's work. Git signing +10, trust backbone +6, identity-log host +4, developer kit +2

In one line: the second-busiest week on record, with the largest rise in automated checks and test stand-ins recorded here, and the unused-code measure getting worse faster than before. Three honest notes. First, 430 against last week's 302 is a real rise: same ten projects, same method. Second, the busiest day is lopsided. Wednesday's 115 includes 49 from git signing alone, where a run of forge-adapter and bridge pull requests landed together. It was one line of work arriving at once, not a stack-wide surge. Third, git signing's +65.6k lines is genuinely new code (two forge adapters, a bridge service and their tests), not code moved in from elsewhere. By contrast, the unused-code markers rose in four projects, not just the one with the new crates. And one about the reading itself: seven of the ten local copies were behind the published main line when this ran, the community app by 15 changes. Every figure on this page comes from origin/main, not from those copies.

The Detail · 10 Projects in Scope

What changed, project by project

The published report tracks around eleven projects. This page covers the ten held locally, so the totals here are a minimum rather than the whole picture. Seven of the ten shipped something this week.

The trust backbone ★

ENGINE
The engine room: identities, communities, credentials, and the agent a person runs. (verifiable-trust-infrastructure)
  • Everything through the signed door: admin, member, config, backup, access-control, endorsement and join operations are now served as signed documents. The admin console signs its own requests with a key issued to the operator's admin identity. A declared proof is required by default. Service management, provisioning, identity hosting and health checks are now signed requests only, and the last legacy routes were removed.
  • Git namespaces: the community side of forge governance. That covers role projection, drift detection with undo or accept, moving a namespace to a new owner, separation of duties with an audited emergency override, account link and unlink, and a Repos screen in the admin console.
  • Security, 18 changes: key custody behind audited, scope-checked doors; refresh-token reuse revokes the session; a second admin's consent for the unrestricted role; no one can widen their own access or grant more than they hold; a caller's data schema can no longer make the service fetch a URL or read a file; no type prints secret material in debug output.
  • Pushes that reach people: member and device pushes go through one durable engine that tries the secure protocol, then encrypted messaging, then plain web. A push that outlives its freshness window is re-issued. A backup now contains the whole agent and restores across plain, hardened and secure-hardware agents.
  • Faces and joining: faces made where they are asked for, retired and tracked; a community can ask an applicant about themselves; invitations can be delivered to the invitee's identity or shown as a QR code.
  • Off main, branch zkp-pcs (13 changes, not counted above): the community half of hidden-vetter admission. It enrols vetters, releases vouching tokens at a published rate, issues one-time challenges, verifies the zero-knowledge proof, and mints the admission credentials, all served as Trust Tasks.
Signed doorGit namespacesTwo-admin consentKey custody 195 changes · +166.1k

The developer kit ★

SDK
The shared toolkit every project here builds on, and the message hub itself. (affinidi-tdk-rs)
  • The hub's admin is signed requests only. The toolkit began signing every request, then the hub began checking, recording each request once, and signing its answers. The legacy admin surface went from switchable to deleted in six days.
  • Two new components: a headless console engine and a terminal console on top of it, with a live traffic view showing each step of a message. It shows sender and recipient, never the message itself, and every admin watcher is audited. Limits can now change without a restart.
  • Delivery settled on the truth: the hub records why each message left, and answers a status request so the sender stops guessing. Secure-protocol messages now route across two hubs, and a message lost to a dropped connection is retried.
  • A sender check tightened: an encrypted message's claimed sender must now match the key that decrypted it.
Legacy admin removedMediator consoleDelivery receiptsSender binding 57 changes · +27.4k

Git signing ★

TOOL
Signs and verifies git commits by identity, and now governs forges. (verifiable-git-infrastructure)
  • Its biggest week since it became a separate project: adapters for GitHub and Forgejo, and a per-community bridge service that applies the community's decisions on the forge and reports back.
  • A change can't approve itself: guards that keep a pull request from satisfying its own check; the bridge posts the check where a forge can't enforce it; platform merges are exempt only when clean; automated dependency updates are re-signed only when the branch still points where it did.
  • The trust question asked privately: the verifier can ask the trust registry over the secure-messaging channels, with the web interface optional. The bridge can run from the community's own agent.
  • Four releases, 0.4.14 through 0.5.1, with the forge crates published from the release tag.
GitHub + ForgejoCommunity bridgeNo self-approval4 releases 80 changes · +65.6k

OpenVTC, the community app ★

APP
The app a person uses to hold identities and join communities. (openvtc)
  • A Repos panel: members see the community repositories they have rights in, resolve drift, and admins see emergency git rights. Creating a repository now names its owners and closes a loophole that let someone grant rights to themselves.
  • Signed, both ways: every request is signed as the persona sending it. Proofs are required on relationship identities, removal notices and community answers. Replies to invitations, join failures and capability requests are authenticated. Issued credentials have their proof checked.
  • Faces for a community: create one where it is needed, retire and reinstate it, see where it has been used, reuse values from a community's own faces, and answer what a community asks through a disclosure.
  • Network checks moved off the screen's main loop, so a slow peer no longer freezes the interface.
  • Off main, branch zkp-pcs (27 changes, not counted above): the client half of hidden-vetter admission. A vetter vouches from the desk without being named, and an applicant builds and submits one proof over the real wire. It includes a reference run and a runbook for standing a community up.
Repos panelSigned both waysFaces 30 changes · +23.3k

The browser wallet ★

APP
The wallet that lives in the browser. (pnm-browser-plugin)
  • Mediator Lens: the relay carrying an agent's mail, seen from inside. Click an identity to see its mail. A standard account is shown the permission it would need to see the relay, and a purge applies only to the plan that was shown.
  • Any identity as a QR code, everywhere an identity appears, so a phone can scan it.
  • Trust only the right sender: a consent result is believed only from the party it answers, a reply only from the peer that was asked, and sender keys are matched by their exact ID. The page a wallet signs for is bound to the site it says it is, and a page can no longer ask the wallet to sign just anything.
  • Faces, in step with the backbone and the community app, and step-up sign-in at an identity-hosting service as signed requests.
Mediator LensQR everywhereSender bindingOrigin binding 46 changes · +11.3k

The identity-log host and the trust registry

SERVICE
Publishing identity histories, and answering “is this party trusted?” (affinidi-webvh-service, affinidi-trust-registry-rs)
  • The identity-log host requires a proof bound to the sender for every control and admin message, and now serves its hosting control plane, its identity management and its web UI's calls as signed requests. A signed sign-in is bound to the service it was addressed to, and the UI's session key can no longer be exported. 14 changes · +19.1k
  • The trust registry authenticates, binds and audits every write and has removed its old admin interface. Every reply is signed with its operational key, and queries now work over the secure protocol too. It cut release 0.20.0. 8 changes · +4.7k
Proof requiredOld admin removedSigned replies 22 changes · +23.8k

The quiet projects

3 SHIPPED NOTHING
Scanned and unchanged, recorded so “unchanged” can be told apart from “not looked at”. (vta-mobile-agent-ios, vti-message-bridge, affinidi-keri-rs)
  • The phone approver shipped nothing for a second week running. This is now more pressing: the approver's core library, which lives in the trust backbone, now signs every request and verifies every reply, and the agent now requires an approver's decision to be signed for the right purpose. The app has to take both changes.
  • The chat bridge was quiet after last week's single change.
  • The KERI library: a fifth consecutive quiet week, as a stable dependency. Unchanged since 23 August.
ScannedApprover must followStable dependency 0 changes
★ What's next · Make the new rules the only rules, everywhere they run

Closing the old doors in the code is done. Closing them in every running deployment is next.

Enforce signatures on the hubs already running. New message hubs are set up to enforce signature checks. Existing ones still default to warn (log it and let it through) until an operator changes the setting. The legacy paths are gone, but a warn-only check is still an open door. Bring the phone approver up to date. Its core library and the agent's approval check both changed this week, and the app itself has shipped nothing for two weeks. It is the standing risk on this page. Run git namespaces with a real community. The setup and operations guide for a community-managed GitHub or Forgejo namespace landed on Friday. The next evidence is a community using one. Land the zero-knowledge work. The zkp-pcs branches work but are local and behind main. The next steps are to bring them up to this week's signed-door stack, swap the hand-written message types for generated ones now that trust-tasks 0.22+ is on main, and get the PCS library reviewed before any community depends on it. And stop the unused-code rise. It is up +22 this week, the third rise in a row, after being named twice. A measure that keeps getting named and keeps rising needs an owner.

🔒
Enforce, don't warn
Existing hubs default to warn-only on bad signatures. Move running deployments to enforce.
📱
The approver follows
Its core now signs everything and the agent checks its decisions. The app has to take both.
🌳
A real namespace
The guide is written. Next is one community governing real repositories with it.
🧹
Give the slip an owner
Unused-code markers are up three weeks running, +22 this week alone.