Skip to content

feat(locallibrary): add account plugins, manifests that say how to sign in to a service - #289

Merged
botandbang merged 14 commits into
mainfrom
bnb/account-plugins
Oct 30, 2026
Merged

botandbang merged 14 commits into
mainfrom
bnb/account-plugins

Conversation

@botandbang

Copy link
Copy Markdown
Contributor

What and why

First step toward signing in to a service once and letting every plugin use that sign-in. Today each plugin declares its own [[contributes.accounts]], so two plugins that use Google sign in twice and keep two sets of tokens. This PR adds the piece the rest builds on: account plugins. Nothing signs in through them yet; the next PR moves plugins onto them.

Added:

  • [[contributes.account_types]] in loclib-plugin (hooks/account_types.rs). It describes how to sign in to a service: endpoints, client, where the token goes, a whoami endpoint with JSON pointers to who signed in, and authorize params for signing in again as one account or adding another (only login_hint, prompt, hd, access_type). It also names scopes, each with the service scopes it asks for and the requests it covers. Scope::covers matches a request against a scope; patterns are written normalised so a trailing * can't reach past a host.
  • Account plugins: a manifest with one account type and nothing else, in plugins/accounts/<id>/ with no crate. build.rs includes them, the workspace excludes the folder, the host never starts one, and plugin serve refuses. Only built-in account plugins may name Local Network's token exchange.
  • Three built-in account plugins: google-account (youtube-upload), mixcloud-account (upload) and soundcloud-account (upload, read). Their request patterns come from the youtube, mixcloud and soundcloud plugins' requests, and those plugins' tests check they're covered.
  • plugin list, plugin info and plugins.describe show account types.
  • apps/locallibrary/GLOSSARY.md (built-in plugin, account plugin, publisher, account, account type, DJ account, default account, scope, access, sign in), a root GLOSSARY-MAP.md, and ADR 0001 on why account plugins are manifests and why only built-in ones use the exchange.
  • Docs: an "Account plugins" section in docs/plugins.md, a NEW_PROJECT.md section, the changelog.

Changed:

  • "First-party plugin" is now "built-in plugin" everywhere (PluginSpec::built_in, built_in.rs, validate(built_in), test names).
  • The accounts hook's endpoint, token place and port checks moved into shared functions so both tables use them.
  • A mistakes property drew a "wrong" token exchange that was sometimes a valid test one; it now always draws a wrong one.

Removed: nothing yet. [[contributes.accounts]] stays until the next PR.

How it was tested

  • New example tests and hegel properties for every account type check: drawn account plugins round-trip through TOML, JSON and the schema; each kind of mistake is refused saying where; a pattern covers exactly the URLs it prefixes (host-suffix and userinfo tricks included). Each new test was broken once to see it fail.
  • youtube and mixcloud tests check their real request URLs are covered by the matching scope. CLI tests cover plugin list, plugin info and plugin serve google-account refusing.
  • Locally: fmt, clippy (workspace and the CLI's two CI feature sets), doctests, rustdoc with -D warnings, ast-grep, tools/policy.sh, and nextest on the workspace. Hegel coverage on this branch: 95.78% lines, 94.20% functions, 93.86% regions; account_types.rs 100%.
  • One failure left, already on main: props_edit::replaying_the_journal_after_a_new_export_redoes_the_edits (a playlist renamed to x//y is accepted by the plan and refused by the model). It gets its own fix PR.

New app or plugin

  • I went through Adding an app or plugin and did every step for this kind of project, and Repo checks passes
  • The steps no check sees are done too: CODEOWNERS if someone else reviews it, a changelog line, and READMEs that say what it does

Checklist

  • Tests cover the change
  • For locallibrary: apps/locallibrary/CHANGELOG.md has a line under Unreleased, if people using it will notice
  • The READMEs, docs/ and --help still say what the code does (apps/locallibrary/docs/rpc/ too, for a sidecar change)

apps/locallibrary/GLOSSARY.md starts locallibrary's own vocabulary (built-in plugin,
account plugin, publisher, account, account type, DJ account, default account, scope,
access, sign in). GLOSSARY-MAP.md lists it beside the website's glossary and notes that
the website's grant and locallibrary's access are unrelated. ADR 0001 records that
account plugins are manifests, that locallibrary runs every sign-in, and that only
built-in account plugins may use Local Network's token exchange.
"Built-in plugin" is the glossary's word for a plugin that ships with locallibrary.
PluginSpec::first_party becomes built_in, and so do the build script's table
(built_in.rs), Manifest::validate's flag, test names and docs. No behaviour change.
…one place

The endpoint, token endpoint, token place and redirect port checks move out of the
accounts hook's validate into functions the account types in the next commit share,
so each error message is written once. No behaviour change.
…t_types]]

An account type says how to sign in to a service: its endpoints and client, where the
token goes, where to ask who signed in, and the params for signing in again as one
account or adding another. It names its scopes, each with the service scopes it asks
for and the requests it covers. A manifest with an account type is an account plugin
and holds nothing else, and only built-in ones may name Local Network's token
exchange. Scope::covers matches a request against a scope. Patterns are written
normalised, so a trailing * can't reach past a host. Example tests and hegel
properties cover each check.
…plugins as manifests

Account plugins live in plugins/accounts/<id>/ as a locallibrary-plugin.toml with no
crate. build.rs reads them and the workspace excludes the folder, so adding one adds a
folder and edits no list. The host never starts one, and plugin serve refuses to.
plugin list, plugin info and plugins.describe show account types. Each scope's
patterns come from the requests the youtube, mixcloud and soundcloud plugins make,
and tests check those requests are covered.
An "Account plugins" section in docs/plugins.md and a changelog entry. Plugins keep
declaring their own accounts until the next change moves them onto account types.
# Conflicts:
#	apps/locallibrary/CHANGELOG.md
#	apps/locallibrary/docs/plugins.md
one_account and another_account were checked against the params the host sets itself,
which left request, request_uri, response_mode, nonce and claims open: request_uri
alone can replace every other param, redirect_uri included. They now take only
login_hint, prompt, hd and access_type.
The mistakes property built a lookalike exchange by replacing localnetwork.computer in
a drawn exchange, but a drawn test exchange on this computer has no such host, so the
"wrong" one was sometimes a valid test exchange and the property failed. It now spells
the lookalike host out.
NEW_PROJECT.md said every plugin is a crate in plugins/<id>/. It now has a section for
account plugins, a manifest alone in plugins/accounts/<id>/, and says the build and the
workspace leave that folder out.
@botandbang
botandbang requested a review from bnb as a code owner October 29, 2026 18:45
@github-actions github-actions Bot added documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file locallibrary locallibrary and the loclib-* crates locallibrary: cli The locallibrary command line locallibrary: sidecar The JSON-RPC sidecar and the api module it serves locallibrary: core The core crate, the data model and rekordbox's formats locallibrary: tracklists Reading tracklists and finding their tracks locallibrary: plugins The plugin protocol, its host and the built-in plugins locallibrary: publishing The Atmosphere account, and the sets and profile published to it tests Tests, fixtures and test harnesses tooling Repo config, lint and build tooling, and GitHub settings labels Oct 29, 2026
# Conflicts:
#	apps/locallibrary/CHANGELOG.md
@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Size

Compared with main. KiB are 1,024 bytes.

This pull request main Change
Client JavaScript 635.1 KiB 635.1 KiB 0 B (0%)
Client JavaScript, brotli 209.5 KiB 209.5 KiB 0 B (0%)
Client CSS 88.6 KiB 88.6 KiB 0 B (0%)
Client CSS, brotli 12.7 KiB 12.7 KiB 0 B (0%)

Comment thread apps/locallibrary/build.rs Fixed
bnb
bnb previously approved these changes Oct 29, 2026
@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Rust coverage

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

Tests Lines Functions Regions
All 97.46% (81950/84089) 96.58% (13053/13515) 95.77% (180170/188130)
Hegel property tests alone 95.19% (80048/84089) 93.75% (12670/13515) 93.3% (175527/188130)

# Conflicts:
#	apps/locallibrary/CHANGELOG.md
@botandbang
botandbang merged commit 83726cb into main Oct 30, 2026
72 checks passed
@botandbang
botandbang deleted the bnb/account-plugins branch October 30, 2026 16:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation locallibrary: cli The locallibrary command line locallibrary: core The core crate, the data model and rekordbox's formats locallibrary: plugins The plugin protocol, its host and the built-in plugins locallibrary: publishing The Atmosphere account, and the sets and profile published to it locallibrary: sidecar The JSON-RPC sidecar and the api module it serves locallibrary: tracklists Reading tracklists and finding their tracks locallibrary locallibrary and the loclib-* crates tests Tests, fixtures and test harnesses tooling Repo config, lint and build tooling, and GitHub settings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants