Skip to content

feat(web): verify accounts on Bluesky from the root account - #202

Merged
botandbang merged 2 commits into
mainfrom
bnb/root-verifications
Oct 24, 2026
Merged

botandbang merged 2 commits into
mainfrom
bnb/root-verifications

Conversation

@botandbang

@botandbang botandbang commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

What and why

Admins can now issue Bluesky verifications (app.bsky.graph.verification) from the root account, localnetwork.computer, under Staff → Verifications. The server already signs in as the root with its account password, so this needs no new credentials or OAuth scopes. Records are written to the root's public repo with the existing root session.

Added:

  • VerificationService lists the root's verifications, reads each account's current handle (checked both ways) and Bluesky display name, and grants or revokes. Granting copies the handle and display name as they are now and refuses an account with no working handle or no display name, since Bluesky never counts those. Re-verifying after a rename updates the existing record in place, because apps count one verification per issuer and account. Only admins can grant or revoke; a moderator's attempt is refused and audited as denied.
  • /admin/verifications (admin-only, in the staff nav): a table of issued verifications with a live status per row that streams in (verified, or out of date with the account's current handle and name), Re-issue and Remove buttons, and a form to verify by handle or DID. It shows an error state with a retry link if the root's records can't be read.
  • Shortcuts: an approved claim whose relationship is "self" or "member" shows a Bluesky verification card with the requester's status and a link to the form, filled in. A manager or agent claim doesn't, since that account belongs to someone other than the act. A project's Managers list shows admins each manager's status with a Verify or Re-issue link. Both leave the shortcut out if the root's records can't be read.
  • listRecords on the root client, which throws when the PDS is down instead of returning nothing, so a failed read can't lead to a duplicate verification.
  • verification.grant and verification.revoke audit actions, with matching knownValues in computer.localnetwork.audit.entry.
  • VerificationTable stories: verified and out of date (wide and phone), checking, nobody verified, and couldn't load.

Changed:

  • The audit action and subject-kind types are now constant lists. A test checks the audit entry lexicon's knownValues against them, and the audit page's action filter is built from the same list instead of a hardcoded copy.
  • app.bsky.graph.verification is validated with the schema @atproto/api ships, alongside strongRef. It isn't published or sent to HappyView.
  • The dev network gives each demo account a Bluesky display name, so verifying works locally.
  • The ROOT_PASSWORD config error asked for an app password; it now asks for the account password, which the root needs to mint space credentials.
  • Coverage thresholds go up to 90 / 78 / 91 / 92 (statements, branches, functions, lines), just under the current numbers.

Bluesky's own app only shows verifications from accounts it has made trusted verifiers, so badges won't appear there until localnetwork.computer is approved. Other apps can choose to trust it now.

After merging, pnpm lexicons:publish publishes the updated audit entry lexicon.

How it was tested

  • Unit tests (verifications.test.ts): granting writes the record to the root's repo, re-issuing after a rename updates it in place, accounts with no working handle or no display name are refused, revoking deletes the record, moderators are denied and audited, unparseable records are skipped, and statuses come back null when the root's records can't be read. They also cover the matching rule against Bluesky's (exact strings, non-empty display name).
  • An audit log test checks the lexicon's knownValues against the code's lists.
  • Integration test on a real PDS: the root writes the verification, its status reads as verified, and revoking deletes it.
  • E2E (Chromium): an admin follows the Verify link from a project's Managers list, verifies the manager, sees them as verified on both pages, and removes the verification, with axe checks.
  • pnpm format:check, pnpm lint, pnpm check, pnpm knip, and pnpm test:coverage.
  • By hand on the dev network: verifying from the Managers list, renaming the account to see it go out of date, and approving a "self" claim to see the shortcut.

Screenshots

1-project-managers-unverified 2-verifications-empty-prefilled 3-verifications-verified 4-project-managers-verified 5-verifications-outdated-wide 6-verifications-outdated-narrow 7-verifications-outdated-phone 8-verifications-outdated-dark 9-claim-approved-verify-shortcut

Checklist

  • Tests cover the change
  • For the website: Storybook stories cover any new UI states
  • The READMEs, docs/ and --help still say what the code does

Admins issue and revoke app.bsky.graph.verification records from the root account at /admin/verifications, with live status per account, re-issue after a rename, and shortcuts from approved self or member claims and from a project's managers. Verifications are audited as verification.grant and verification.revoke, and a test keeps the audit entry lexicon's knownValues in step with the code's lists.
@botandbang
botandbang requested a review from bnb as a code owner October 23, 2026 16:36
@github-actions github-actions Bot added documentation Improvements or additions to documentation web The Local Network website (apps/web) lexicons The computer.localnetwork.* lexicons web: staff Admin, moderation and the audit log web: server Spaces, sync, HappyView, the database and the server around them web: ui Shared components, design tokens, layout and the static pages tests Tests, fixtures and test harnesses tooling Repo config, lint and build tooling, and GitHub settings labels Oct 23, 2026
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Coverage Report for apps/web

Status Category Percentage Covered / Total
🟢 Lines 92.72% (🎯 92%)
⬆️ +0.13 pp
4987 / 5378
🟢 Statements 91.12% (🎯 90%)
⬆️ +0.14 pp
7691 / 8440
🟢 Functions 92.32% (🎯 91%)
⬆️ +0.10 pp
1948 / 2110
🟢 Branches 79.17% (🎯 78%)
⬆️ +0.19 pp
3493 / 4412
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
apps/web/src/lib/nsid.ts 100%
🟰 ±0 pp
100%
🟰 ±0 pp
100%
🟰 ±0 pp
100%
🟰 ±0 pp
apps/web/src/lib/verification.ts 100% 100% 100% 100%
apps/web/src/lib/components/StatusPill.svelte 100%
🟰 ±0 pp
66.66%
⬆️ +16.66 pp
100%
🟰 ±0 pp
100%
🟰 ±0 pp
apps/web/src/lib/components/VerificationTable.svelte 97.18% 80.76% 93.75% 100% 58
apps/web/src/lib/server/app.ts 64.78%
⬆️ +0.25 pp
42.85%
🟰 ±0 pp
48.57%
🟰 ±0 pp
72.13%
⬆️ +0.23 pp
87, 119, 137-152, 161, 163, 166, 183, 220-233, 295, 305, 312, 315-316, 325, 331, 346-362, 369, 378-381, 391-396
apps/web/src/lib/server/config.ts 83.33%
🟰 ±0 pp
83.33%
🟰 ±0 pp
90.9%
🟰 ±0 pp
86.04%
🟰 ±0 pp
173-183
apps/web/src/lib/server/atproto/lexicons.ts 97.87%
⬆️ +0.05 pp
89.47%
🟰 ±0 pp
93.33%
⬆️ +0.48 pp
100%
🟰 ±0 pp
132
apps/web/src/lib/server/atproto/repo-client.ts 86.25%
⬆️ +2.69 pp
52.63%
🟰 ±0 pp
87.5%
⬆️ +4.17 pp
89.04%
⬆️ +2.68 pp
181-182, 224, 268-269, 278, 286, 305, 341
apps/web/src/lib/server/audit/log.ts 94.7%
⬆️ +0.05 pp
85.79%
🟰 ±0 pp
96.29%
🟰 ±0 pp
100%
🟰 ±0 pp
180, 301, 307, 352, 354, 381, 485, 533, 557, 574
apps/web/src/lib/server/services/verifications.ts 98.3% 96.87% 93.33% 97.82% 53
apps/web/src/lib/server/testing/core.ts 93.1%
⬆️ +0.38 pp
76%
⬆️ +2.09 pp
100%
🟰 ±0 pp
98.11%
⬆️ +0.11 pp
67, 76, 83, 106
apps/web/src/lib/server/testing/fakes.ts 87.85%
⬆️ +0.35 pp
77.33%
🟰 ±0 pp
93.02%
⬆️ +0.52 pp
93.49%
⬆️ +0.22 pp
82, 93, 215-223, 230, 232, 255-259, 278, 329
Generated in workflow #354 for commit 5fa39be by the Vitest Coverage Report Action

Size

Compared with main. KiB are 1,024 bytes.

This pull request main Change
Client JavaScript 591.7 KiB 585.6 KiB +6.1 KiB (+1%)
Client JavaScript, brotli 196.1 KiB 193.7 KiB +2.4 KiB (+1.2%)
Client CSS 86.5 KiB 86.3 KiB +120 B (+0.1%)
Client CSS, brotli 12.5 KiB 12.5 KiB +16 B (+0.1%)

@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Rust coverage

From run 342. Both are held to the thresholds in .cargo/config.toml.

Tests Lines Functions Regions
All 97.37% (77560/79651) 96.43% (12283/12738) 95.66% (170356/178086)
Hegel property tests alone 95.42% (76002/79651) 93.88% (11958/12738) 93.5% (166509/178086)

@botandbang
botandbang merged commit 74fd960 into main Oct 24, 2026
83 of 99 checks passed
@botandbang
botandbang deleted the bnb/root-verifications branch October 24, 2026 00:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation lexicons The computer.localnetwork.* lexicons tests Tests, fixtures and test harnesses tooling Repo config, lint and build tooling, and GitHub settings web: server Spaces, sync, HappyView, the database and the server around them web: staff Admin, moderation and the audit log web: ui Shared components, design tokens, layout and the static pages web The Local Network website (apps/web)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant