From 7d8eeafbf2b8144db695112ecb80981bf3960b93 Mon Sep 17 00:00:00 2001 From: Geoff Turk Date: Thu, 22 Oct 2026 22:42:05 +0200 Subject: [PATCH 1/7] Add Explore 03: put a GitHub organization under the VTC with the VGI bridge A new explore page after the walkthrough: build the config-session cnm, give it a community-administrator identity, install and set up vgi-bridge in VTA mode behind Nginx, point the VTC at it, register the GitHub App, bind the organization, give the cnm identity a passkey for single-administrator self-grants, adopt a repository, grant rights, and what members do in OpenVTC to link, clone and sign. Verified on the 2026-10-22 nightly with vgi-bridge 0.20.0. The Known Issues section records what bit during that run: the keyring cnm build, the step-up link carrying /v1 (fixed in verifiable-trust-infrastructure#2267), a did-git-sign credential missing from the VTA's ACL, an empty repository's first branch becoming the protected default, and a registry started before the VTA. Signed-off-by: Geoff Turk --- sysop/explore/02-walkthrough.md | 4 + .../03-verifiable-git-infrastructure.md | 466 ++++++++++++++++++ sysop/explore/README.md | 1 + 3 files changed, 471 insertions(+) create mode 100644 sysop/explore/03-verifiable-git-infrastructure.md diff --git a/sysop/explore/02-walkthrough.md b/sysop/explore/02-walkthrough.md index 643f4f5..460b383 100644 --- a/sysop/explore/02-walkthrough.md +++ b/sysop/explore/02-walkthrough.md @@ -875,6 +875,10 @@ Navigate to `https://vtc.yourdomain.com/admin` and sign in with your new passkey You now have access to the VTC Admin Dashboard and can set up access for the community manager(s) of the VTC. +## Next + +Put a GitHub organization under the community: [03 — Verifiable Git Infrastructure](03-verifiable-git-infrastructure.md). + ## Known Issues / Edge Cases ### DID Enrollment URL is single-use diff --git a/sysop/explore/03-verifiable-git-infrastructure.md b/sysop/explore/03-verifiable-git-infrastructure.md new file mode 100644 index 0000000..936dccd --- /dev/null +++ b/sysop/explore/03-verifiable-git-infrastructure.md @@ -0,0 +1,466 @@ +# Explore 03: Verifiable Git Infrastructure + +Put a GitHub organization under the VTC you stood up in [02 — Walkthrough](02-walkthrough.md): the community decides who owns, who may merge and who may commit to each repository, publishes the commit rights to its Trust Registry, and a **bridge** turns those decisions into GitHub roles and a required pull-request check. Members link their GitHub accounts and sign commits with keys their VTA holds. + +The shape to hold in your head: **the VTC decides, the bridge acts, the registry answers.** Rights are records in the VTC. The bridge, `vgi-bridge`, is the one service holding the community's GitHub App; the VTC sends it jobs over TSP or DIDComm and never talks to GitHub itself. CI reads the registry. Nobody edits GitHub by hand. + +> **⚠️ Explore stream — do not use for real keys.** +> Same box, same rules as the walkthrough: everything runs as root under `/root//` ([why root?](01-server-setup.md#why-root)). Run every command on this page from a root shell on the box unless it says otherwise. + +**Tested on:** Ubuntu Server 26.04 ([Explore 01: Server Setup](01-server-setup.md)) + +**Verified with:** + +| Build | VTA | VTC | DID Hosting Daemon | `vgi-bridge` | `cnm` | `did-git-sign` | +| --- | --- | --- | --- | --- | --- | --- | +| Nightly, 2026-10-22 | 0.64.0 | 0.12.0 | 0.11.0 | 0.20.0 (release) | 0.38.0 | 0.18.2 | + +The mediator and Trust Registry were the same day's nightly builds. The bridge is a tagged release of [verifiable-git-infrastructure](https://github.com/OpenVTC/verifiable-git-infrastructure) (VGI); its own operator guide is [SETUP-GITHUB-VTC.md](https://github.com/OpenVTC/verifiable-git-infrastructure/blob/main/docs/SETUP-GITHUB-VTC.md), and the VTC side is [git-namespaces.md](https://github.com/OpenVTC/verifiable-trust-infrastructure/blob/main/docs/03-vtc/git-namespaces.md). This page is the explore-box walk through both. + +## Prerequisites + +- [01 — Server setup](01-server-setup.md) and [02 — Walkthrough](02-walkthrough.md) are complete, and the stack is running. If the box has rebooted since, start the services **in this order, waiting for each to come up**: DID Hosting Daemon, Mediator, VTA, Trust Registry, VTC. A registry started before the VTA boots from its cached bundle and reports `degraded`, and a degraded registry means no git right ever reaches CI. `curl -s http://127.0.0.1:3232/health` must answer `{"status":"OK","writes":"ok"}` before you go on. +- A **GitHub organization you own**. The Free plan works; the merge gate is then a check the bridge posts itself. On Team or Enterprise the bridge installs an organization required workflow instead. A personal account also works, with less (the bridge cannot create repositories there); the VGI guide's §9.1 covers the differences. +- A DNS **A record** for the bridge, `bridge.yourdomain.com`, pointing at the box, DNS only like the others. A wildcard record covers it. +- A browser where you are signed in to GitHub as an organization owner and to the VTC admin console (the passkey from Step 6 of the walkthrough). + +This page uses the IDs the walkthrough had you save, plus these new ones: + +| ID | What to Save | Used In | +| --- | --- | --- | +| 1b | VTA DID (walkthrough) | Step 4 | +| 6a | VTC DID (walkthrough) | Steps 2 & 4 | +| 7a | `cnm` community identity DID | Steps 2, 8 & 9 | +| 7b | Bridge DID | Step 5 | +| 7c | Namespace id (`ns_…`) | Steps 9 & 10 | + +## Steps + +### Step 1: Install a server build of `cnm` + +Every change on this page is a signed Trust Task sent by `cnm`, the Community Network Manager. The default `cnm` build keeps its session in the OS keyring, which a headless box does not have, and stops with `the OS credential store is unavailable`. You need the **config-session** build, the counterpart of the `pnm-server` build the walkthrough installed. It keeps the session in `/root/.config/cnm/sessions.json`, in plaintext, which is acceptable on the explore box and nowhere else. + +A pre-built `cnm-server` binary is not on the download server yet, so build it. The checkout from [Server Setup, Option B](01-server-setup.md#option-b-build-from-source) is at `~/fpp/verifiable-trust-infrastructure` if you built from source; otherwise clone it now, on the `nightly` branch to match the binaries you downloaded: + +```bash +mkdir -p ~/fpp && cd ~/fpp +git clone --depth 1 --branch nightly https://github.com/OpenVTC/verifiable-trust-infrastructure.git +cd verifiable-trust-infrastructure +cargo build --release -p cnm-cli --no-default-features --features config-session +install -m 0755 target/release/cnm /usr/local/bin/cnm +``` + +On the recommended 2 vCPU / 4 GB box the build takes 20 to 40 minutes. If you would rather build on a faster Linux machine and copy the binary over, build it the same way there, and check before copying that it asks for no newer glibc than the box has (`ldd --version` on the box; `objdump -T target/release/cnm | grep -o 'GLIBC_[0-9.]*' | sort -Vu | tail -1` on the binary), since the two must agree. + +**Check:** + +```bash +cnm community list +``` + +> **Expected result:** `No communities configured.`, with a note that sessions are stored as plaintext. The DBus error means you still have the keyring build. + +### Step 2: Give `cnm` an identity at the VTC + +`cnm` acts as a `did:key` of its own, which must be a **community administrator** at the VTC: binding a namespace needs that authority, and so do the elevated grants later. The identity is minted on the box, granted while the VTC is stopped, and rotated to a fresh key once it has signed in. + +Mint it, naming the **VTC DID** (6a): + +```bash +cnm community add "My Community" --slug mine --vtc-did +``` + +It prints the grant routes and one JSON line ending in `"state":"pending"`. Copy the `Admin DID` (`did:key:z6Mk…`). + +Stop the VTC and grant that DID offline (the command opens the VTC's store directly, which it can only do while the daemon is stopped): + +```bash +kill $(pgrep -x vtc) +cd ~/vtc +vtc --config config.toml acl add --did --role admin --label cnm +nohup vtc > log.txt 2>&1 & +``` + +> **Expected result:** `Added ACL entry for did:key:… (role admin; community-admin: everything).` The VTC audits an offline write as a break-glass at its next start and lists it under Actions in the console; that is expected. + +Once `https://vtc.yourdomain.com` answers again, finish the profile. `continue` signs in as the granted key and immediately rotates to a new one, so the DID you just pasted loses all standing: + +```bash +cnm community continue mine +cnm community use mine +``` + +> **⚠️ SAVE THIS** (7a) +> +> Save the DID in `continue`'s last line, `cnm authenticates to it as did:key:…`. That is the **`cnm` community identity** (7a): it will hold the first namespace admin right, and every grant you make is signed by it. + +**Check:** + +```bash +cnm git namespace list +``` + +> **Expected result:** `No namespaces.` A `notAdministrator` refusal means the ACL grant did not land; `permissionDenied` or an authentication error means `continue` did not complete. + +### Step 3: Install the bridge + +The bridge is a single binary from the VGI release. Download the tarball and its checksum, verify, install: + +```bash +mkdir -p ~/vgi-bridge-dl && cd ~/vgi-bridge-dl +curl -fsSLO https://github.com/OpenVTC/verifiable-git-infrastructure/releases/download/v0.20.0/vgi-bridge-x86_64-unknown-linux-gnu.tar.gz +curl -fsSLO https://github.com/OpenVTC/verifiable-git-infrastructure/releases/download/v0.20.0/vgi-bridge-x86_64-unknown-linux-gnu.tar.gz.sha256 +sha256sum -c vgi-bridge-x86_64-unknown-linux-gnu.tar.gz.sha256 +tar xzf vgi-bridge-x86_64-unknown-linux-gnu.tar.gz +install -m 0755 vgi-bridge-x86_64-unknown-linux-gnu/vgi-bridge /usr/local/bin/vgi-bridge +vgi-bridge --help +``` + +The binary needs `libdbus-1-3` and `git` at run time; the setup script installed both. The release also carries a build provenance attestation, checkable with `gh attestation verify` from a machine that has `gh`; the VGI README has the command. + +GitHub sends webhooks and browser redirects to the bridge, so it needs an HTTPS front like the other services. Add an Nginx site in the same shape the setup script wrote for them, then get its certificate: + +```bash +cat > /etc/nginx/sites-available/bridge.conf <<'NGX' +server { + listen 80; + server_name bridge.yourdomain.com; + client_max_body_size 2m; + + location /healthz { + allow 127.0.0.1; + deny all; + proxy_pass http://127.0.0.1:8080; + } + + location / { + proxy_pass http://127.0.0.1:8080; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $host; + } +} +NGX +ln -sf /etc/nginx/sites-available/bridge.conf /etc/nginx/sites-enabled/bridge.conf +nginx -t && systemctl reload nginx +certbot --nginx -d bridge.yourdomain.com --non-interactive --agree-tos +``` + +Replace `yourdomain.com` in the `server_name` line and the `certbot` command. The `/healthz` block keeps the health endpoint off the public internet, as the bridge's guide asks. + +> **Expected result:** Certbot reports `Successfully deployed certificate for bridge.yourdomain.com`. `https://bridge.yourdomain.com/` answers `502 Bad Gateway` until the bridge runs. + +### Step 4: Set up the bridge + +`vgi-bridge setup` writes the whole bridge folder: its configuration, a credential for a trust context of its own in the VTA, and the bridge's `did:webvh`, minted into that context on your DID Hosting Daemon. The bridge then keeps its secrets and state in the VTA, and the box holds only the credential, so a lost box is recovered by issuing a new one. It needs the **VTC DID** (6a), the **VTA DID** (1b), the bridge's public URL and the organization's GitHub login, in lowercase: + +```bash +vgi-bridge setup \ + --vtc \ + --vta \ + --dir /root/vgi-bridge \ + --public-url https://bridge.yourdomain.com/ \ + --owner \ + --service none +``` + +Setup resolves the Trust Registry and the mediator from the VTC's DID document and prints them, fetches GitHub's `web-flow` key, pins the check to this release's commit, then prints a temporary `did:key` and two `pnm` commands, and waits: + +```text +A temporary admin DID for this setup (held in memory only): + did:key:z6Mk… + + If the context `vgi-bridge` does not exist yet: + pnm contexts create --id vgi-bridge --name "vgi-bridge" \ + --admin-did did:key:z6Mk… --admin-expires 1h --admin-handoff + +Press Enter once one of them has run (Ctrl+C to stop)… +``` + +In a second root shell, run the **first** command exactly as printed (the context is new). Then press Enter in the setup shell. Setup authenticates over TSP, has the VTA roll the temporary grant over to a long-term credential scoped to the `vgi-bridge` context, mints the bridge's DID, and runs its checks: + +```text + wrote /root/vgi-bridge/vta-credential.json (0600): did:key:… +Wrote /root/vgi-bridge/bridge.toml + minted the bridge's DID did:webvh:…:dids.yourdomain.com: + ok context `vgi-bridge` exists, DID `did:webvh:…` + ok the credential fetches the DID's keys, and its current document lists 2 of them … + ok the key secrets are sealed under (`vgi-bridge/app-state-seal`) is in the context and exports + ok the credential writes, reads and deletes app-state (`vgi-bridge`) + ok the credential reaches `vgi-bridge` only + +The bridge's DID: + did:webvh:…:dids.yourdomain.com: +``` + +> **⚠️ SAVE THIS** (7b) +> +> Save **The bridge's DID** (7b). `vgi-bridge --config /root/vgi-bridge/bridge.toml identity show` prints it again. + +The folder now holds `bridge.toml` (nothing secret in it), `vta-credential.json` (owner-only), `web-flow.asc` and `data/`. With `--service none` setup wrote no service file; run the bridge under systemd so it survives a reboot and comes back after the VTA does: + +```bash +cat > /etc/systemd/system/vgi-bridge.service <<'UNIT' +[Unit] +Description=VGI bridge (VTC-governed git namespaces) +After=network-online.target nginx.service +Wants=network-online.target + +[Service] +Type=simple +WorkingDirectory=/root/vgi-bridge +ExecStart=/usr/local/bin/vgi-bridge --config /root/vgi-bridge/bridge.toml run +Restart=always +RestartSec=10 +StandardOutput=append:/root/vgi-bridge/bridge.log +StandardError=append:/root/vgi-bridge/bridge.log + +[Install] +WantedBy=multi-user.target +UNIT +systemctl daemon-reload +systemctl enable --now vgi-bridge.service +``` + +`Restart=always` matters after a reboot: the bridge needs its keys from the VTA, retries for five minutes, then exits, and systemd starts it again. (If you prefer `nohup` like the other services, the command is `vgi-bridge --config /root/vgi-bridge/bridge.toml run`.) + +**Check:** + +```bash +curl -s http://127.0.0.1:8080/healthz +grep -E "connected to the mediator|register\?state=" /root/vgi-bridge/bridge.log | tail -2 +``` + +> **Expected result:** `ok`, a line `connected to the mediator`, and a WARN naming a one-time registration link, `https://bridge.yourdomain.com/github/github.com//register?state=…`. Keep that link for Step 6; the bridge prints a fresh one at every start until an App is registered. + +### Step 5: Point the VTC at the bridge + +The VTC refuses a bridge-mode bind for a forge it has no bridge for. Tell it which bridge serves `github.com`, with the **bridge DID** (7b). The walkthrough's `vtc setup` already wrote a `[git_ns]` section with an empty `[git_ns.bridges]` table; add one line under it: + +```bash +cd ~/vtc +vi config.toml +``` + +```toml +[git_ns] +elevated_requires_admin = true +tick_seconds = 5 + +[git_ns.bridges] +"github.com" = "" +``` + +If your config has no `[git_ns]` section at all, add the whole block. Unknown keys fail the start, so a typo shows up at once. The section is read only at start, so restart the VTC: + +```bash +kill $(pgrep -x vtc) +nohup vtc > log.txt 2>&1 & +``` + +**Check:** `cnm git namespace list` still answers `No namespaces.`, and `grep "git-ns projector" ~/vtc/log.txt` shows `git-ns projector starting … registry=true`. + +### Step 6: Register the GitHub App and enable Device Flow + +The bridge registers its own GitHub App through GitHub's manifest flow, so no one copies a private key by hand. Two things happen in the browser, **as an owner of the organization**: + +1. Open the registration link from the bridge log (Step 4). The page posts the App manifest to GitHub; review it and create the App. GitHub redirects back to the bridge, which stores the App's id, private key and webhook secret sealed in the VTA. +2. Open `https://github.com/organizations//settings/apps/-vgi-bridge`, tick **Enable Device Flow**, and save. The manifest cannot turn this on, and members cannot link their GitHub accounts without it. + +**Check:** + +```bash +grep -E "is registered for|refused a registration|registration failed" /root/vgi-bridge/bridge.log | tail -1 +``` + +> **Expected result:** a line of the form +> +> ```text +> the GitHub App `-vgi-bridge` is registered for `` on github.com and in service (app_id …) +> ``` + +### Step 7: Bind the organization + +Bind the community to the organization. `--mode` defaults to `manual` (no bridge, rights only), so say `bridge`: + +```bash +cnm git namespace bind --forge github.com --owner --mode bridge +``` + +`cnm` first says what binding makes public: every right granted in the namespace is published to the community's Trust Registry, where anyone can read who owns and who may commit to each repository. Then it prints the namespace as `pending` with an installation link: + +```text +github.com/ — pending (ns_…) +Prove control of the owner on the forge to finish binding: + https://github.com/apps/-vgi-bridge/installations/new?state=… +``` + +> **⚠️ SAVE THIS** (7c) +> +> Save the **namespace id** (7c), the `ns_…` in parentheses. + +**An organization owner opens that link within 15 minutes** and installs the App, choosing **All repositories**. The bridge must see every repository the community adopts or creates; one outside the installation's selection does not exist to it. GitHub redirects back to the bridge, which completes the bind. The VTC then marks the namespace bound, gives the `cnm` identity (7a) the first `git.ns.admin`, and grants the bridge a `git.commit.sign` service grant on the namespace so the commits it re-signs for Dependabot pass. + +**Check:** + +```bash +cnm git namespace list +cnm --json git namespace list +``` + +> **Expected result:** `github.com/ ns_… bridge bound admins: 1 repos: 0`. In the JSON, `forgeStatus.appRegistration` is `registered`, `missingPermissions` is empty, and `roleMapSource` is `reported`. On a Free organization `orgRulesets` is `false` and `bridgePostedCheck` is `true`; that is the merge gate you will get. If `missingPermissions` is not empty, the owner declined something at install; accept it at `https://github.com/organizations//settings/installations`. + +The same namespace is now on the admin console at `https://vtc.yourdomain.com/admin` under **Community → Repos**. The page reads and writes only signed Trust Tasks, so the first visit asks you to **Set up signing** (a console key approved with your passkey) before it shows anything. + +### Step 8: Give the `cnm` identity a passkey + +The walkthrough set the community up in **single-administrator mode**. In that mode you may grant yourself an elevated right (`git.repo.own`, `git.repo.create`, `git.ns.admin`), which separation of duties would otherwise leave to a second administrator, by answering a passkey step-up bound to that one request. The `cnm` identity (7a) has no passkey yet, so any such grant is refused with `step-up required: … holds no step-up factor at this community`. Mint it a passkey invite now, while the VTC is stopped, the same way the walkthrough re-minted the install URL: + +```bash +kill $(pgrep -x vtc) +cd ~/vtc +vtc --config config.toml admin invite --did --ttl 3600 +nohup vtc > log.txt 2>&1 & +``` + +It prints an **Install URL** and a **Claim code** and leaves the existing ACL entry untouched. Once the VTC is back, open the URL in your browser, enter the code, and register a passkey. It is recorded against the `cnm` identity. + +You can skip this step if every elevated right will go to **other** members, never to the `cnm` identity itself. + +### Step 9: Adopt a repository + +An existing repository is **adopted**; a new one is created with `cnm git create`. Adoption records the owners and has the bridge inspect the repository and set up whatever the merge gate needs. Owners must be **current members** of the community, named by DID; the walkthrough's join flow gave your persona one (`cnm access list` lists every entry). Adopt is an elevated action, so under the default configuration the `cnm` identity signs it as a community administrator: + +```bash +cnm git adopt github.com// --owner +``` + +Naming the `cnm` identity itself as an owner is a self-grant. The first send is refused with a step-up request, and `cnm` prints a link to open in the browser: + +```text +Open this in a browser and confirm with your passkey … + https://vtc.yourdomain.com/v1/admin/step-up#request=… +Paste the answer code the page shows and press Enter; if the page says the gesture is recorded, just press Enter (within five minutes). +``` + +> **The link may be wrong in `cnm` 0.38.0 and earlier.** It is built on the API base and so carries `/v1`, which this VTC answers with the community website. Open `https://vtc.yourdomain.com/admin/step-up#request=…` instead, keeping everything after `#` exactly as printed. Fixed in [verifiable-trust-infrastructure#2267](https://github.com/OpenVTC/verifiable-trust-infrastructure/pull/2267). + +Confirm with the passkey from Step 8, then press Enter in the shell. `cnm` sends the identical document again and reports `Single-administrator waiver applied`; the VTC writes a Critical audit row, by design. + +On a Free organization the bridge then sets up the **bridge-posted check**: no workflow in the repository, the bridge runs the commit check itself and posts *Verify commit trust* on every pull request, and a repository ruleset **`VGI commit trust`** on the default branch requires a pull request, requires that check, and forbids force-pushes and deletion, with no bypass list. + +**Check:** + +```bash +cnm git repos --namespace +cnm --json git repos --namespace +``` + +> **Expected result:** `github.com// active owners: 1 sync: inSync`. In the JSON, `guard` is `bridgePostedCheck`, `driftCount` is `0`, and `steps` shows `requiredCheck` as `applied`. On GitHub, the repository's *Settings → Rules → Rulesets* lists `VGI commit trust`, active, with no bypass actors. + +### Step 10: Grant rights + +Rights, from broad to narrow. `own` implies `maintain` implies `commit.sign`; `ns.admin` implies everything in the namespace. + +| Right | Resource | Lets the holder | Who signs | +| --- | --- | --- | --- | +| `git.ns.admin` | `github.com/` | everything below on every repository; adopt; unbind | a community administrator | +| `git.repo.create` | `github.com/` | create repositories and own them | a community administrator | +| `git.repo.own` | `github.com//` | grant and revoke the rights below; transfer; archive; GitHub `admin` | a community administrator | +| `git.repo.maintain` | `github.com//` | merge and triage; GitHub `maintain` | an owner or namespace admin | +| `git.commit.sign` | a repository or the namespace | author commits the check accepts; no GitHub role | an owner or namespace admin | + +Give the namespace a second admin first, so it never depends on one identity, then grant as the community needs. Subjects must be current members; resources are forge-qualified and lowercase: + +```bash +cnm git grant --subject --right git.ns.admin --resource github.com/ --reason "second namespace admin" +cnm git grant --subject --right git.repo.maintain --resource github.com// --reason "maintainer" +cnm git grant --subject --right git.commit.sign --resource github.com// --expires-in 365d +cnm git revoke --subject --right git.commit.sign --resource github.com// --reason "left the project" +``` + +A grant reaches the registry within seconds and GitHub when the bridge next runs its job. `cnm git view --admin` lists every recorded right with who granted it and why; the console's Repos page and each member's page show the same. + +## Members: link, clone, sign + +Each contributor does this once, in **OpenVTC** on their own machine ([developer guides](../../developer/)). Open the community and press `r` for its **Repos** view. It shows the member's rights, their linked forge account and the state of commit signing on that machine. + +1. **Link a GitHub account** (`l`). The bridge keys GitHub roles on the account's numeric id, so an owner or maintainer gets `admin` or `maintain` on the repository only once linked. GitHub shows a device code to enter at `https://github.com/login/device`; approve the App when asked. Commit-only members need no link. +2. **Set up signing** (`s`). This runs `did-git-sign init` against the member's VTA: a one-hour `pnm` grant that hands off to a long-term credential scoped to the persona's context, then the persona and signing key to use. `init` writes no git configuration; signing is switched on per repository, or per directory of repositories, by one `include.path` line, so a contributor's existing GPG or SSH signing key keeps working everywhere else. +3. **Clone** (`c`). Owners and maintainers clone the repository directly; no fork is needed, since the bridge gave them a GitHub role. Members holding only `git.commit.sign` contribute from a fork. The clone lands under the workspace the view names (`~/src` by default, `w` to change) with signing enabled for it. + +Then the ordinary flow, on a branch, because the `VGI commit trust` ruleset protects the default branch: + +```bash +git switch -c first-change +# edit, then +git commit -m "Say hello" +git log -1 --format='%(trailers:key=Signed-by-DID)' +git push -u origin first-change +gh pr create --fill +``` + +The trailer names the member's persona DID; `did-git-sign` sent the commit's digest to the VTA, which signed it, and the private key never left the VTA. On the pull request the bridge posts *Verify commit trust*; it passes when every commit's signer is published in the registry for that repository or its namespace, and the merge button unlocks. **Merge with a merge commit**, not squash or rebase: those produce commits signed by GitHub's `web-flow` key that the check rejects the next time it sees them. + +GitHub's own **Verified** badge is a separate axis. The commit is validly signed either way; the badge appears only if the member also uploads the persona's Ed25519 public key to their GitHub account as a **Signing Key** (Settings → SSH and GPG keys → New SSH key, key type *Signing Key*). The key is the `ssh-ed25519 …` part of the matching line in `~/.config/did-git-sign/allowed_signers`. Do not also enable GitHub's built-in *Require signed commits* rule: it knows only keys registered with GitHub and would block every DID-signed commit from a member who has not uploaded theirs. + +## Verification + +- `curl -s http://127.0.0.1:3232/health` answers `{"status":"OK","writes":"ok"}`, and `grep "projection pass" ~/vtc/log.txt | tail -1` shows `failed=0`. +- `cnm git namespace list` shows the namespace `bridge bound` with no missing permissions. +- `cnm --json git repos --namespace <7c>` shows the repository `active`, `inSync`, `driftCount 0`. +- The admin console's **Community → Repos** page shows the namespace card and the repository; a granted member sees the repository and their right at `https://vtc.yourdomain.com/members/#/repos`. +- A signed pull request passes *Verify commit trust*; a commit made with `git -c commit.gpgsign=false commit` fails it and cannot be merged. + +## Known Issues / Edge Cases + +### `cnm` fails with a DBus or keyring error + +You have the default keyring build. Install the config-session build from [Step 1](#step-1-install-a-server-build-of-cnm). + +### The step-up link opens the community website + +`cnm` 0.38.0 and earlier print the link as `https://vtc.yourdomain.com/v1/admin/step-up#request=…`. Remove the `/v1` and keep the fragment: `https://vtc.yourdomain.com/admin/step-up#request=…`. A step-up request lives five minutes; if it lapsed, run the `cnm` command again for a fresh link. Fixed in [verifiable-trust-infrastructure#2267](https://github.com/OpenVTC/verifiable-trust-infrastructure/pull/2267). + +### `step-up required: … holds no step-up factor at this community` + +The identity that signed the command has no passkey registered at the VTC, so the single-administrator waiver cannot ask for its gesture. Do [Step 8](#step-8-give-the-cnm-identity-a-passkey), or name a different member as the subject. + +### `git-ns/namespace/bind:noBridge` + +The VTC has no `[git_ns.bridges]` entry for `github.com`, or it was not restarted after the entry was added ([Step 5](#step-5-point-the-vtc-at-the-bridge)). + +### `permissionDenied` on bind, or `notAdministrator` on every listing + +The `cnm` identity is not a community administrator. Repeat the offline `vtc acl add` from [Step 2](#step-2-give-cnm-an-identity-at-the-vtc) with the DID `cnm community list` shows as `Identity`. + +### A member's link attempt fails at the device code + +Device Flow is not enabled on the App ([Step 6](#step-6-register-the-github-app-and-enable-device-flow)). + +### `git commit` fails with `DID not in ACL: did:key:…` from `did-git-sign` + +The credential `did-git-sign init` provisioned has no ACL entry at the VTA that holds the persona, usually because the one-hour setup grant lapsed before the hand-off completed. Grant that credential, scoped to the persona's OpenVTC context and narrowed to signing, from a machine with `pnm` admin on that VTA: + +```bash +pnm --vta acl create --did --role admin --contexts --capabilities sign-sshsig --label "did-git-sign" +``` + +`did-git-sign health` shows the credential DID and the VTA; `pnm --vta contexts list` shows which context holds the persona. Then `git config --global did-git-sign.signer vta` so it never falls back to exporting the key, and `did-git-sign verify` for a test signature. + +### The first push to an empty repository became the default branch + +An empty repository makes the first pushed branch its default, and the `VGI commit trust` ruleset follows the default branch, so a branch pushed as `first` is now protected and there is nothing to open a pull request against. Create `main` from the same commit, make it the default, and the ruleset moves with it: + +```bash +git push origin first:main +gh repo edit / --default-branch main +git push origin --delete first +``` + +Creating a branch is not restricted by the ruleset, so the push succeeds; from then on changes reach `main` by pull request. + +### The Trust Registry reports `degraded` after a reboot + +It started before the VTA or the DID Hosting Daemon was serving, could not reach the mediator, and booted from its cached bundle with its DIDComm listener stopped. Restart it once the VTA answers `https://vta.yourdomain.com/health`, and check `/health` again. The VTC's log then shows `trust-registry health probe recovered`. diff --git a/sysop/explore/README.md b/sysop/explore/README.md index 6145af8..447ba69 100644 --- a/sysop/explore/README.md +++ b/sysop/explore/README.md @@ -11,3 +11,4 @@ Read these in order: 1. [01 — Server setup](01-server-setup.md) — create the Ubuntu host, configure DNS, run `setup-explore.sh`. 2. [02 — Walkthrough](02-walkthrough.md) — step through each tool's TUI wizard, provision the Trust Registry with `pnm`, save the IDs each step asks you to save. +3. [03 — Verifiable Git Infrastructure](03-verifiable-git-infrastructure.md) — put a GitHub organization under the VTC: install the VGI bridge, register its GitHub App, bind the organization, adopt a repository, grant members rights, and sign commits with VTA-held keys. From 932de343881fd0f37ae41c6c9b836a31f1f3e6a6 Mon Sep 17 00:00:00 2001 From: Geoff Turk Date: Fri, 23 Oct 2026 09:54:57 +0200 Subject: [PATCH 2/7] Explore 03: install cnm-server from the download server The devops build pipeline now publishes the config-session build of cnm as cnm-server, next to pnm-server (ic3software/devops#150). Step 1 downloads it the way Server Setup downloads the rest of the stack, with the same no-checksum caveat, and keeps the source build only as the fallback for a box that asks before the next nightly has run. The Verified-with note says the run itself used a source build of the same nightly commit. Signed-off-by: Geoff Turk --- .../explore/03-verifiable-git-infrastructure.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/sysop/explore/03-verifiable-git-infrastructure.md b/sysop/explore/03-verifiable-git-infrastructure.md index 936dccd..884fb0c 100644 --- a/sysop/explore/03-verifiable-git-infrastructure.md +++ b/sysop/explore/03-verifiable-git-infrastructure.md @@ -15,7 +15,7 @@ The shape to hold in your head: **the VTC decides, the bridge acts, the registry | --- | --- | --- | --- | --- | --- | --- | | Nightly, 2026-10-22 | 0.64.0 | 0.12.0 | 0.11.0 | 0.20.0 (release) | 0.38.0 | 0.18.2 | -The mediator and Trust Registry were the same day's nightly builds. The bridge is a tagged release of [verifiable-git-infrastructure](https://github.com/OpenVTC/verifiable-git-infrastructure) (VGI); its own operator guide is [SETUP-GITHUB-VTC.md](https://github.com/OpenVTC/verifiable-git-infrastructure/blob/main/docs/SETUP-GITHUB-VTC.md), and the VTC side is [git-namespaces.md](https://github.com/OpenVTC/verifiable-trust-infrastructure/blob/main/docs/03-vtc/git-namespaces.md). This page is the explore-box walk through both. +The mediator and Trust Registry were the same day's nightly builds. `cnm` was the config-session build of that nightly's commit, compiled from source before the download server carried it. The bridge is a tagged release of [verifiable-git-infrastructure](https://github.com/OpenVTC/verifiable-git-infrastructure) (VGI); its own operator guide is [SETUP-GITHUB-VTC.md](https://github.com/OpenVTC/verifiable-git-infrastructure/blob/main/docs/SETUP-GITHUB-VTC.md), and the VTC side is [git-namespaces.md](https://github.com/OpenVTC/verifiable-trust-infrastructure/blob/main/docs/03-vtc/git-namespaces.md). This page is the explore-box walk through both. ## Prerequisites @@ -40,7 +40,17 @@ This page uses the IDs the walkthrough had you save, plus these new ones: Every change on this page is a signed Trust Task sent by `cnm`, the Community Network Manager. The default `cnm` build keeps its session in the OS keyring, which a headless box does not have, and stops with `the OS credential store is unavailable`. You need the **config-session** build, the counterpart of the `pnm-server` build the walkthrough installed. It keeps the session in `/root/.config/cnm/sessions.json`, in plaintext, which is acceptable on the explore box and nowhere else. -A pre-built `cnm-server` binary is not on the download server yet, so build it. The checkout from [Server Setup, Option B](01-server-setup.md#option-b-build-from-source) is at `~/fpp/verifiable-trust-infrastructure` if you built from source; otherwise clone it now, on the `nightly` branch to match the binaries you downloaded: +The download server publishes it as `cnm-server`, next to `pnm-server`, from the same nightly build. The same caveat as [Server Setup, Option A](01-server-setup.md#option-a-download-pre-built-binaries-recommended) applies: no checksum or signature, so an explore box only. + +```bash +cd ~ +curl -fO https://download.firstperson.dev/cnm-server/nightly/cnm +chmod +x cnm && mv cnm /usr/local/bin/ +``` + +For a tagged release, use `cnm-server/latest/cnm` instead, matching the channel the rest of the stack came from. Do not "correct" the path to `cnm/`: that is the keyring build. + +If `curl` answers `404`, the server build is not published yet (it joined the build pipeline on 2026-10-23 and appears with the next nightly). Build it instead, from the checkout at `~/fpp/verifiable-trust-infrastructure` if you used [Option B](01-server-setup.md#option-b-build-from-source), or from a fresh clone of the `nightly` branch to match the binaries you downloaded: ```bash mkdir -p ~/fpp && cd ~/fpp @@ -50,7 +60,7 @@ cargo build --release -p cnm-cli --no-default-features --features config-session install -m 0755 target/release/cnm /usr/local/bin/cnm ``` -On the recommended 2 vCPU / 4 GB box the build takes 20 to 40 minutes. If you would rather build on a faster Linux machine and copy the binary over, build it the same way there, and check before copying that it asks for no newer glibc than the box has (`ldd --version` on the box; `objdump -T target/release/cnm | grep -o 'GLIBC_[0-9.]*' | sort -Vu | tail -1` on the binary), since the two must agree. +On the recommended 2 vCPU / 4 GB box that build takes 20 to 40 minutes. If you would rather build on a faster Linux machine and copy the binary over, build it the same way there, and check before copying that it asks for no newer glibc than the box has (`ldd --version` on the box; `objdump -T target/release/cnm | grep -o 'GLIBC_[0-9.]*' | sort -Vu | tail -1` on the binary), since the two must agree. **Check:** From a9c09dfc9cf28cffd00b61fc0be55c8a41be3c90 Mon Sep 17 00:00:00 2001 From: Geoff Turk Date: Fri, 23 Oct 2026 10:00:49 +0200 Subject: [PATCH 3/7] Install cnm-server in Server Setup, and have Explore 03 only check it Explore 01's Step 5 now installs cnm-server beside pnm-server, in both download channels and in the Option B source build, so the box has the config-session cnm whether it was built or downloaded. Explore 03's Step 1 drops its own download and source-build instructions and the 404 fallback, and only checks that the installed cnm is the server build; its Known Issues entry points back to Server Setup. Signed-off-by: Geoff Turk --- sysop/explore/01-server-setup.md | 13 ++++++++ .../03-verifiable-git-infrastructure.md | 32 +++---------------- 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/sysop/explore/01-server-setup.md b/sysop/explore/01-server-setup.md index d445540..4a48532 100644 --- a/sysop/explore/01-server-setup.md +++ b/sysop/explore/01-server-setup.md @@ -143,6 +143,11 @@ chmod +x vtc && mv vtc /usr/local/bin/ curl -O https://download.firstperson.dev/pnm-server/nightly/pnm chmod +x pnm && mv pnm /usr/local/bin/ +# cnm-server: the same server build of CNM, the community administrator's +# tool, used from this box in Explore 03. Same rule: not the cnm/ path. +curl -O https://download.firstperson.dev/cnm-server/nightly/cnm +chmod +x cnm && mv cnm /usr/local/bin/ + curl -O https://download.firstperson.dev/mediator/nightly/mediator chmod +x mediator && mv mediator /usr/local/bin/ @@ -173,6 +178,11 @@ chmod +x vtc && mv vtc /usr/local/bin/ curl -O https://download.firstperson.dev/pnm-server/latest/pnm chmod +x pnm && mv pnm /usr/local/bin/ +# cnm-server: the same server build of CNM, the community administrator's +# tool, used from this box in Explore 03. Same rule: not the cnm/ path. +curl -O https://download.firstperson.dev/cnm-server/latest/cnm +chmod +x cnm && mv cnm /usr/local/bin/ + curl -O https://download.firstperson.dev/mediator/latest/mediator chmod +x mediator && mv mediator /usr/local/bin/ @@ -208,8 +218,11 @@ git checkout VTI-Eucalyptus # latest tagged release, or 'git checkout nightly' f cargo install --path vta-service --no-default-features --features "setup,config-seed,didcomm,rest,cli-synthesis" cargo install --path vtc-service --no-default-features --features "setup,config-secret,website,admin-ui" cargo install --path pnm-cli --no-default-features --features "config-session,tsp" +cargo install --path cnm-cli --no-default-features --features "config-session" ``` +`pnm` and `cnm` are built with `config-session` in place of their default `keyring` feature, so their sessions go to a plaintext file instead of an OS keyring, which a headless host has no access to. `cnm-cli` has no `tsp` feature to add back; its TSP comes with `vtc-client`. + #### Mediator ```bash diff --git a/sysop/explore/03-verifiable-git-infrastructure.md b/sysop/explore/03-verifiable-git-infrastructure.md index 884fb0c..6e8917f 100644 --- a/sysop/explore/03-verifiable-git-infrastructure.md +++ b/sysop/explore/03-verifiable-git-infrastructure.md @@ -15,7 +15,7 @@ The shape to hold in your head: **the VTC decides, the bridge acts, the registry | --- | --- | --- | --- | --- | --- | --- | | Nightly, 2026-10-22 | 0.64.0 | 0.12.0 | 0.11.0 | 0.20.0 (release) | 0.38.0 | 0.18.2 | -The mediator and Trust Registry were the same day's nightly builds. `cnm` was the config-session build of that nightly's commit, compiled from source before the download server carried it. The bridge is a tagged release of [verifiable-git-infrastructure](https://github.com/OpenVTC/verifiable-git-infrastructure) (VGI); its own operator guide is [SETUP-GITHUB-VTC.md](https://github.com/OpenVTC/verifiable-git-infrastructure/blob/main/docs/SETUP-GITHUB-VTC.md), and the VTC side is [git-namespaces.md](https://github.com/OpenVTC/verifiable-trust-infrastructure/blob/main/docs/03-vtc/git-namespaces.md). This page is the explore-box walk through both. +The mediator and Trust Registry were the same day's nightly builds. The bridge is a tagged release of [verifiable-git-infrastructure](https://github.com/OpenVTC/verifiable-git-infrastructure) (VGI); its own operator guide is [SETUP-GITHUB-VTC.md](https://github.com/OpenVTC/verifiable-git-infrastructure/blob/main/docs/SETUP-GITHUB-VTC.md), and the VTC side is [git-namespaces.md](https://github.com/OpenVTC/verifiable-trust-infrastructure/blob/main/docs/03-vtc/git-namespaces.md). This page is the explore-box walk through both. ## Prerequisites @@ -36,31 +36,9 @@ This page uses the IDs the walkthrough had you save, plus these new ones: ## Steps -### Step 1: Install a server build of `cnm` +### Step 1: Check the server build of `cnm` -Every change on this page is a signed Trust Task sent by `cnm`, the Community Network Manager. The default `cnm` build keeps its session in the OS keyring, which a headless box does not have, and stops with `the OS credential store is unavailable`. You need the **config-session** build, the counterpart of the `pnm-server` build the walkthrough installed. It keeps the session in `/root/.config/cnm/sessions.json`, in plaintext, which is acceptable on the explore box and nowhere else. - -The download server publishes it as `cnm-server`, next to `pnm-server`, from the same nightly build. The same caveat as [Server Setup, Option A](01-server-setup.md#option-a-download-pre-built-binaries-recommended) applies: no checksum or signature, so an explore box only. - -```bash -cd ~ -curl -fO https://download.firstperson.dev/cnm-server/nightly/cnm -chmod +x cnm && mv cnm /usr/local/bin/ -``` - -For a tagged release, use `cnm-server/latest/cnm` instead, matching the channel the rest of the stack came from. Do not "correct" the path to `cnm/`: that is the keyring build. - -If `curl` answers `404`, the server build is not published yet (it joined the build pipeline on 2026-10-23 and appears with the next nightly). Build it instead, from the checkout at `~/fpp/verifiable-trust-infrastructure` if you used [Option B](01-server-setup.md#option-b-build-from-source), or from a fresh clone of the `nightly` branch to match the binaries you downloaded: - -```bash -mkdir -p ~/fpp && cd ~/fpp -git clone --depth 1 --branch nightly https://github.com/OpenVTC/verifiable-trust-infrastructure.git -cd verifiable-trust-infrastructure -cargo build --release -p cnm-cli --no-default-features --features config-session -install -m 0755 target/release/cnm /usr/local/bin/cnm -``` - -On the recommended 2 vCPU / 4 GB box that build takes 20 to 40 minutes. If you would rather build on a faster Linux machine and copy the binary over, build it the same way there, and check before copying that it asks for no newer glibc than the box has (`ldd --version` on the box; `objdump -T target/release/cnm | grep -o 'GLIBC_[0-9.]*' | sort -Vu | tail -1` on the binary), since the two must agree. +Every change on this page is a signed Trust Task sent by `cnm`, the Community Network Manager. [Server Setup, Step 5](01-server-setup.md#step-5-install-service-binaries) installed its **config-session** build, `cnm-server`, the counterpart of `pnm-server`: the default build keeps its session in the OS keyring, which a headless box does not have, while this one keeps it in `/root/.config/cnm/sessions.json`, in plaintext, which is acceptable on the explore box and nowhere else. **Check:** @@ -68,7 +46,7 @@ On the recommended 2 vCPU / 4 GB box that build takes 20 to 40 minutes. If you w cnm community list ``` -> **Expected result:** `No communities configured.`, with a note that sessions are stored as plaintext. The DBus error means you still have the keyring build. +> **Expected result:** `No communities configured.`, with a note that sessions are stored as plaintext. A DBus or `OS credential store is unavailable` error means the keyring build is installed; go back to Server Setup, Step 5, and install `cnm-server` (the `cnm-server/` download path, or the `cargo install` line for `cnm-cli`). ### Step 2: Give `cnm` an identity at the VTC @@ -427,7 +405,7 @@ GitHub's own **Verified** badge is a separate axis. The commit is validly signed ### `cnm` fails with a DBus or keyring error -You have the default keyring build. Install the config-session build from [Step 1](#step-1-install-a-server-build-of-cnm). +You have the default keyring build. Install `cnm-server` as in [Server Setup, Step 5](01-server-setup.md#step-5-install-service-binaries), then rerun the check in [Step 1](#step-1-check-the-server-build-of-cnm). ### The step-up link opens the community website From cc67b11a502012bb413ad33defdcd87552ee04f3 Mon Sep 17 00:00:00 2001 From: Geoff Turk Date: Mon, 2 Nov 2026 09:20:18 +0100 Subject: [PATCH 4/7] Explore 03: add an "Upgrading the bridge" section The bridge is released often, mostly to track the VTA, the mediator and the VTC's Trust Task versions, so a bridge left on an older release stops working against a newer stack. The new section walks an in-place upgrade: download and verify the release tarball, stop the service and swap the binary keeping the old one for rollback, move the verify-trust pin in bridge.toml (setup pins it to its own release and nothing updates it), start again and check after a short wait, with what the log should show. It also notes that repositories bootstrapped under the old pin keep the old workflow until the VTC resends their bootstrap job. A Known Issues entry maps the minute-by-minute "TSP revision mismatch" warning, which a 0.20.0 bridge logs once the mediator speaks TSP 1.0, to that section. Verified upgrading 0.20.0 to 0.30.0 on 2026-11-02. Signed-off-by: Geoff Turk --- .../03-verifiable-git-infrastructure.md | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/sysop/explore/03-verifiable-git-infrastructure.md b/sysop/explore/03-verifiable-git-infrastructure.md index 6e8917f..fac8511 100644 --- a/sysop/explore/03-verifiable-git-infrastructure.md +++ b/sysop/explore/03-verifiable-git-infrastructure.md @@ -401,6 +401,57 @@ GitHub's own **Verified** badge is a separate axis. The commit is validly signed - The admin console's **Community → Repos** page shows the namespace card and the repository; a granted member sees the repository and their right at `https://vtc.yourdomain.com/members/#/repos`. - A signed pull request passes *Verify commit trust*; a commit made with `git -c commit.gpgsign=false commit` fails it and cannot be merged. +## Upgrading the bridge + +The bridge is released often, and most releases exist to track the VTA, the mediator and the Trust Task versions the VTC speaks, so a bridge left behind stops working against a newer stack before anything in its own feature set changes. The versions in `cargo search vgi-bridge`, the [releases page](https://github.com/OpenVTC/verifiable-git-infrastructure/releases) and the newest tag can briefly disagree while a release is being built; the release page is the one with the tarball. Verified with the upgrade from 0.20.0 to 0.30.0 on 2026-11-02. + +Before upgrading, check the box's VTA is new enough for the release: since bridge 0.26.0 the bridge pages its context's keys and app-state with Trust Task versions that need vta-service 0.67 or later ([BRIDGE.md, *The VTA's version*](https://github.com/OpenVTC/verifiable-git-infrastructure/blob/main/docs/BRIDGE.md)). The nightly builds [Server Setup](01-server-setup.md) installs are. Everything under `/root/vgi-bridge` carries over: the configuration, the VTA credential, the bridge's DID and the state it keeps in the VTA. Nothing is set up again. + +**1. Download and verify the release**, as in [Step 3](#step-3-install-the-bridge), into a fresh directory: + +```bash +V=0.30.0 +mkdir -p ~/vgi-bridge-dl-$V && cd ~/vgi-bridge-dl-$V +curl -fsSLO https://github.com/OpenVTC/verifiable-git-infrastructure/releases/download/v$V/vgi-bridge-x86_64-unknown-linux-gnu.tar.gz +curl -fsSLO https://github.com/OpenVTC/verifiable-git-infrastructure/releases/download/v$V/vgi-bridge-x86_64-unknown-linux-gnu.tar.gz.sha256 +sha256sum -c vgi-bridge-x86_64-unknown-linux-gnu.tar.gz.sha256 +tar xzf vgi-bridge-x86_64-unknown-linux-gnu.tar.gz +``` + +**2. Swap the binary.** Stop the bridge first: its store allows one process at a time. Keep the old binary beside the new one, so a rollback is the same swap the other way: + +```bash +systemctl stop vgi-bridge.service +cp /usr/local/bin/vgi-bridge /usr/local/bin/vgi-bridge.previous +install -m 0755 vgi-bridge-x86_64-unknown-linux-gnu/vgi-bridge /usr/local/bin/vgi-bridge +vgi-bridge --version +``` + +**3. Move the verify-trust pin.** [Step 4](#step-4-set-up-the-bridge)'s setup pinned the CI action in `bridge.toml` to the commit of the release it was run from, and nothing updates it, so until it is moved every bootstrap keeps writing the old verifier into repositories. The pin is the commit of the new release's tag: + +```bash +SHA=$(curl -fsSL -H 'Accept: application/vnd.github+json' https://api.github.com/repos/OpenVTC/verifiable-git-infrastructure/commits/v$V | python3 -c 'import json,sys; print(json.load(sys.stdin)["sha"])') +echo $SHA +cp /root/vgi-bridge/bridge.toml /root/vgi-bridge/bridge.toml.bak +sed -i -e "s|verify-trust@[0-9a-f]\{40\}|verify-trust@$SHA|" -e "s|^version = \"v[0-9.]*\"|version = \"v$V\"|" /root/vgi-bridge/bridge.toml +grep -n -A3 '^\[verify_trust\]' /root/vgi-bridge/bridge.toml +``` + +> **Expected result:** a 40-hex `SHA`, and the `[verify_trust]` section showing `action = "OpenVTC/verifiable-git-infrastructure/.github/actions/verify-trust@"` and `version = "v0.30.0"`. If either line kept its old value, edit it by hand. + +**4. Start it again**, then give it a few seconds before checking: the bridge opens its listener only after it has reached the VTA, so a check in the same breath as the start answers `Connection refused`. + +```bash +systemctl start vgi-bridge.service +sleep 5 +curl -s http://127.0.0.1:8080/healthz +grep -E "starting the VGI bridge|connected to the mediator|revision mismatch" /root/vgi-bridge/bridge.log | tail -3 +``` + +> **Expected result:** `ok`, a `starting the VGI bridge` line and `connected to the mediator` from the new process, and no further `TSP revision mismatch` lines. A `send failed; the outbox will retry` or `not connected to the mediator` in the moment before `connected to the mediator` is the start-up race the outbox exists for, not a fault. + +Repositories bootstrapped under the old pin keep the old workflow: the bridge does not report an outdated workflow as drift, so nothing re-sends the bootstrap on its own. Have the VTC send the repository's bootstrap job again (`cnm git repos --namespace <7c>` lists them) when you next touch the namespace. + ## Known Issues / Edge Cases ### `cnm` fails with a DBus or keyring error @@ -449,6 +500,10 @@ git push origin --delete first Creating a branch is not restricted by the ruleset, so the push succeeds; from then on changes reach `main` by pull request. +### `could not check for rotated keys … TSP revision mismatch` every minute + +The mediator was upgraded to a newer TSP revision than the bridge speaks (`message is 0.2, this build speaks 1.0`); the bridge keeps its current keys in service but can no longer list them, and will not see a rotation. [Upgrade the bridge](#upgrading-the-bridge). + ### The Trust Registry reports `degraded` after a reboot It started before the VTA or the DID Hosting Daemon was serving, could not reach the mediator, and booted from its cached bundle with its DIDComm listener stopped. Restart it once the VTA answers `https://vta.yourdomain.com/health`, and check `/health` again. The VTC's log then shows `trust-registry health probe recovered`. From eab8769fa723f1d2125d0363ba5563873e2ba196 Mon Sep 17 00:00:00 2001 From: Geoff Turk Date: Mon, 2 Nov 2026 09:26:13 +0100 Subject: [PATCH 5/7] Explore 03: the verify-trust pin changes nothing under the bridge-posted check The upgrade section said to have the VTC resend a repository's bootstrap job after moving the pin, but on this page's Free-plan setup the guard is the check the bridge posts itself, so no repository carries a workflow to refresh. Say so, name the guards where the pin does matter, and be honest that no cnm command sends a bootstrap on demand: the VTC sends one at adoption, after an inspection finds a step missing, on a protectionChanged event that drops the required check, or for a drift revert of drift the bridge reported. Point at BRIDGE.md's per-guard upgrade steps for those. Signed-off-by: Geoff Turk --- sysop/explore/03-verifiable-git-infrastructure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysop/explore/03-verifiable-git-infrastructure.md b/sysop/explore/03-verifiable-git-infrastructure.md index fac8511..ab3a7bf 100644 --- a/sysop/explore/03-verifiable-git-infrastructure.md +++ b/sysop/explore/03-verifiable-git-infrastructure.md @@ -450,7 +450,7 @@ grep -E "starting the VGI bridge|connected to the mediator|revision mismatch" /r > **Expected result:** `ok`, a `starting the VGI bridge` line and `connected to the mediator` from the new process, and no further `TSP revision mismatch` lines. A `send failed; the outbox will retry` or `not connected to the mediator` in the moment before `connected to the mediator` is the start-up race the outbox exists for, not a fault. -Repositories bootstrapped under the old pin keep the old workflow: the bridge does not report an outdated workflow as drift, so nothing re-sends the bootstrap on its own. Have the VTC send the repository's bootstrap job again (`cnm git repos --namespace <7c>` lists them) when you next touch the namespace. +On this page's setup the pin changes nothing already in place: a Free-plan organization has the bridge-posted check as its guard, which the bridge runs with its own binary, so no repository carries a verify-trust workflow to refresh. The pin matters where the guard is a workflow, an organization required workflow on Team or Enterprise, an in-repo workflow, or Forgejo. There a repository bootstrapped under the old pin keeps the old workflow, the bridge does not report that as drift, and no `cnm` command sends a bootstrap on demand: the VTC sends one at adoption, after an inspection finds a step missing, when a `protectionChanged` event removes the required check, or for a `cnm git drift resolve … revert` of drift the bridge has reported. [BRIDGE.md, *Upgrading*](https://github.com/OpenVTC/verifiable-git-infrastructure/blob/main/docs/BRIDGE.md) has the per-guard steps. ## Known Issues / Edge Cases From d76234c687927614feeab80ba7ff14cc045f5a66 Mon Sep 17 00:00:00 2001 From: Geoff Turk Date: Mon, 2 Nov 2026 09:29:42 +0100 Subject: [PATCH 6/7] Explore 03: say the page is a one-off run, kept as a guide for an AI The page records one walk through the bridge on the 2026-10-22 builds and will not be re-verified as the bridge, the VTC and cnm move on. A callout at the top says so, and tells a reader who wants the latest versions to have their AI assistant read the page for the shape of the work, then read the current VGI and VTI repositories to find which versions go together and carry out the install from those, with the Upgrading section as the worked example of that approach. Signed-off-by: Geoff Turk --- sysop/explore/03-verifiable-git-infrastructure.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sysop/explore/03-verifiable-git-infrastructure.md b/sysop/explore/03-verifiable-git-infrastructure.md index ab3a7bf..5150daa 100644 --- a/sysop/explore/03-verifiable-git-infrastructure.md +++ b/sysop/explore/03-verifiable-git-infrastructure.md @@ -7,6 +7,9 @@ The shape to hold in your head: **the VTC decides, the bridge acts, the registry > **⚠️ Explore stream — do not use for real keys.** > Same box, same rules as the walkthrough: everything runs as root under `/root//` ([why root?](01-server-setup.md#why-root)). Run every command on this page from a root shell on the box unless it says otherwise. +> **📌 A one-off run on an older build, kept as a guide for your AI.** +> This page records one walk through the bridge on the builds in the table below, and it will not be updated as those move on. The bridge, the VTC and `cnm` have all released since, and the commands, prompts and outputs here will drift from what the latest versions do. To get onto the latest versions, do not follow the page step by step: have your AI assistant read it for the shape of the work, the order of the steps, the identifiers to save and the checks to run, then look at the current state of [verifiable-git-infrastructure](https://github.com/OpenVTC/verifiable-git-infrastructure) (its releases, `docs/BRIDGE.md` and `docs/SETUP-GITHUB-VTC.md`) and [verifiable-trust-infrastructure](https://github.com/OpenVTC/verifiable-trust-infrastructure) (`docs/03-vtc/git-namespaces.md` and the `cnm git` command help), work out which versions go together, and carry out the install from those. The [Upgrading the bridge](#upgrading-the-bridge) section shows what that digging looks like for one upgrade. + **Tested on:** Ubuntu Server 26.04 ([Explore 01: Server Setup](01-server-setup.md)) **Verified with:** From 06e43fe10e7a3930bbe355d2b27ab1541870aa77 Mon Sep 17 00:00:00 2001 From: Geoff Turk Date: Mon, 2 Nov 2026 09:30:01 +0100 Subject: [PATCH 7/7] Explore 03: join the two top callouts into one blockquote A blank line between two blockquotes is one blockquote with a blank line inside it, which markdownlint rejects (MD028). A `>` continuation line keeps the explore-stream warning and the one-off-run note together. Signed-off-by: Geoff Turk --- sysop/explore/03-verifiable-git-infrastructure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysop/explore/03-verifiable-git-infrastructure.md b/sysop/explore/03-verifiable-git-infrastructure.md index 5150daa..7bd0d78 100644 --- a/sysop/explore/03-verifiable-git-infrastructure.md +++ b/sysop/explore/03-verifiable-git-infrastructure.md @@ -6,7 +6,7 @@ The shape to hold in your head: **the VTC decides, the bridge acts, the registry > **⚠️ Explore stream — do not use for real keys.** > Same box, same rules as the walkthrough: everything runs as root under `/root//` ([why root?](01-server-setup.md#why-root)). Run every command on this page from a root shell on the box unless it says otherwise. - +> > **📌 A one-off run on an older build, kept as a guide for your AI.** > This page records one walk through the bridge on the builds in the table below, and it will not be updated as those move on. The bridge, the VTC and `cnm` have all released since, and the commands, prompts and outputs here will drift from what the latest versions do. To get onto the latest versions, do not follow the page step by step: have your AI assistant read it for the shape of the work, the order of the steps, the identifiers to save and the checks to run, then look at the current state of [verifiable-git-infrastructure](https://github.com/OpenVTC/verifiable-git-infrastructure) (its releases, `docs/BRIDGE.md` and `docs/SETUP-GITHUB-VTC.md`) and [verifiable-trust-infrastructure](https://github.com/OpenVTC/verifiable-trust-infrastructure) (`docs/03-vtc/git-namespaces.md` and the `cnm git` command help), work out which versions go together, and carry out the install from those. The [Upgrading the bridge](#upgrading-the-bridge) section shows what that digging looks like for one upgrade.