A CLI for building, sharing, and federating Calibre library catalogs — over HTTP, Tor, or S3-compatible storage.
Throughout, alice is a librarian (an operator-hosted
federation member) and bob another (a peer member);
fed is a federation, and op is the operator's
host (it appears in serve URLs like
http://op:8800).
The CLI is lean: you declare a thing once, then refer to it by name.
Most commands read the same way: a resource, the action on it, then a name for this run of that action:
accorder lib build aliceaccorder fedlib share live fedThe resource (lib or fedlib) and verb
(build or share) say what to do; the
trailing name is yours to choose, so you remember what this run was
for.
That name is the action alias — a memorable context
for the action. The first time, you spell the action out in flags and
accorder saves them under the name. (A few per-run switches
are better left one-off, so they are not saved — see below.)
Once you have configured the action with flags, you run it bare — the saved config is what executes.
For the lib resource — a Calibre library — the action
alias names one of your libraries. With only one, you need no name; it
takes the implicit default. With several, the name tells
them apart: research, comicbooks,
fiction…
If you run:
accorder lib build alice --librarian "Alice Wonderland" --calibrepath ~/Calibre/alice
both the librarian name and the path to Alice's Calibre library are saved.
Next time it is enough to run just:
accorder lib build alice
and both values are read from the configuration, as if the flags were provided.
Change your mind at any point by passing just the one flag you want different: it overwrites that single value, the rest stays put, and you can forget again.
Add private mode on top of the already-saved librarian
(Alice Wonderland) and Calibre path
(~/Calibre/alice):
accorder lib share live alice --mode private
It runs in private mode and saves the mode for future runs.
Next time all three flags are implicitly present:
accorder lib share live alice
Changing one flag runs with its new value plus the saved ones, and saves the new value for future bare runs:
accorder lib share live alice --calibrepath ~/CalibreLibraries/alice
The invariant to hold in your head: a bare run replays the
latest values you set — omitting a flag means "what I last
set," not a fresh default. This covers the alias-persisted flags;
deliberate per-run switches — --offline,
--only-http, --only-tor,
--insecure — are never saved and must be given each
time.
accorder config show alice prints the saved flag tree
for one alias — the stored values a bare run replays (run-time defaults,
environment values, and descriptor/credential resolution aren't part of
that picture). accorder config list names the aliases
you've built up so far (alice, bob,
fed) and the action families saved under each.
Some flags span a resource's actions: give --calibrepath
once (at your first lib command, step 1) and the alias's
other shared-group lib actions — browse,
build, import, join,
publish, status, sync,
upload, share live — inherit it
(lib download is the exception: its
--calibrepath is a plain destination directory, set on that
action).
--librarian and --libraryID are adopted
before they are generated: if any action under the alias already carries
one, that value is taken and spread to the rest; only when none does is
a new one minted. An empty field on the action you happen to run is not
the same as the library having no identity.
Federations do the same through the descriptor
(fedlib config set) — but note the positional there is a
federation id, not an action alias:
fedlib config set, fedlib members invite, and
fedlib members offer key the descriptor by that id (a
separate store from the aliases above). fedlib build and
fedlib share live take a normal action alias; when it names
a descriptor (as fed does here), they recall its bucket,
S3, paths, and keys-file.
Browse, search, and download from the web — no catalog to maintain or re-share. The reader surfs the web:
https://library.memoryoftheworld.org
That page is the public face of a federation aggregate. Once the operator has set it up (step 2), serving it is a single line — the descriptor and alias remember the bucket, S3, paths, and listen address:
accorder fedlib share live motw
And it is more than a web page: in this most-public mode a browser
gets plain browsing, but pointing accorder at the same
endpoint turns the reader into a librarian — caching the index,
selecting offline, rendering into your own Calibre, re-sharing. That is
the rest of this guide.
accorder lib browse)A friend is a librarian, keen to share their library with you. Or, as
with Memory of the World, a group of friends — amateur
librarians — run an aggregate of many libraries and share it with the
world.
You browse a shared library and render your selection into your own Calibre library. The catalog-to-be needs two things: a Calibre path and a librarian name. A library name is optional — it labels your rendered catalog and travels to anyone who later browses yours. After running:
accorder lib browse alice --endpoint https://library.memoryoftheworld.org
the shared library's index is downloaded, cached, and bookmarked as
alice's last source. A bare lib browse alice reopens
it.
Each browsed source is also registered by name
(lib index list shows them), and any of them can be browsed
with --from <name> without disturbing the
bookmark.
The lib browse command opens a web browser on the shared
library.
Until the Calibre path and librarian name are set, the header's SETTINGS step collects them first, and the other steps stay locked.
From there the header walks you through three steps:
MY LIBRARY until you name it) — click it and the downloaded
books are rendered into your Calibre-ready
catalog.Both the Calibre path and the librarian name can be handed over on
the command line (-c and -l), so a
fully-flagged browse skips SETTINGS entirely — only an
optional library name is left to set there. Both are saved and shared
with every later lib action:
accorder lib browse alice --endpoint https://library.memoryoftheworld.org --calibrepath ~/Calibre/alice --librarian alice
If the aggregate is shared in private mode, alice needs an invite
from the operator and passes an --invite blob in place of
the URL:
accorder lib browse alice --invite 'accorder-invite:…'
Reopen alice's last-browsed source with no network: the cached index gives you full browsing and search offline. A selection lives only in the running session — to fetch files, come back online and download from QUEUE; then, as always, the header's last step — the one named after your catalog — renders the downloaded books into it.
accorder lib browse alice --offline
accorder lib build)Building a catalog needs only two flags: --librarian and
--calibrepath. Coming from lib browse above,
both are already on the alias — each from its flag (-c,
-l) or SETTINGS, the librarian
auto-generated (as is the UUID) when left unset — so alice builds
with:
accorder lib build alice
The browse route is not required: starting from an already existing Calibre library, the same build just needs those two flags spelled out:
accorder lib build alice --librarian alice --calibrepath ~/Calibre/alice
Or the books are in Zotero. lib import syncs a
BetterBibTeX export into the Calibre library — adding what is new,
updating what changed, leaving the rest alone — and
--calibrepath is already shared, so only the export
directory is new:
accorder lib import alice --zotero-path /tmp/betterbibtex-from-zotero/
Re-run it whenever the export changes. A book Zotero no longer carries is reported, not removed, until you ask:
accorder lib import alice --prune
--prune and --yes are per-run like the
switches above — never saved — so a bare lib import alice
cannot delete anything you did not ask for.
After the library changes, alice rebuilds with just:
accorder lib build alice
accorder lib share live)Serve the library so others can lib browse it. In a
-tags tor build it comes up on both a Tor onion and HTTP
(--only-http narrows to clearnet); the default build serves
HTTP only. --mode gates access.
accorder lib share live alice --only-http --listen 0.0.0.0:8900 --mode private
A bare restart replays the saved listen and mode:
accorder lib share live alice
--only-http is per-run and never saved, so on a
Tor-enabled build repeat it (the default build is HTTP-only anyway).
While it serves, alice's terminal prints a ready-made command for her
friend. She does not run it — she copies it and sends it to
bob:
accorder lib browse local-librarian --from remote-alice --endpoint http://192.168.1.24:8900/
The two names say which side each belongs to: local-
is the friend's own context, remote- is alice's
library. Alice cannot know who will receive the line, so the local half
stays the generic local-librarian — a word inviting
replacement.
bob puts his own alias in place of the suggested one and
runs it:
accorder lib browse local-bob --from remote-alice --endpoint http://192.168.1.24:8900/
Read it as three answers:
local-bob is his local setup — his librarian
name, Calibre path, and the action alias (local-bob), saved
in the configuration for next time;remote-alice is his name for the remote
library, whose index he searches, selects from, queues, and downloads to
render into his local-bob catalog;--endpoint is where that library lives.Next time bob needs only his alias:
accorder lib browse local-bob
Or he can reach alice's library while working under a different alias — a separate local Calibre library, but the same source library and index:
accorder lib browse local-bob-2 --from remote-alice
Both names were only suggestions. Bob can use any alias — omitting it
means default — rename the source with
lib index rename, or drop --from once it is
saved. On a loopback bind, no such line is printed — nothing off-machine
could reach it.
Granting a friend access prints an invite blob; they browse your share into their own library — step 1, from the other side.
Alice prepares the invitation for her friend bob:
accorder lib share grant alice --friend bob --endpoint http://alice-host:8900
bob gets his invitation from alice and can
then build his own library:
accorder lib browse bob --invite 'accorder-invite:…' --calibrepath ~/Calibre/bob
accorder fedlib)Stand up an aggregate and serve it; this comes before joining because
the invite/offer minted here is what a librarian joins with. The
descriptor (fedlib config set) is remembered, so the rest
stays short.
accorder fedlib config set fed --bucket motw/fed --s3-provider Wasabi --s3-endpoint s3.wasabisys.com --keys-file keys/op.s3.keys --mode public --fedlib-path ~/agg --upload-prefix aggregate
fedlib members invite mints an operator-hosted member's
blob. Auto-minting the member's scoped S3 creds needs the operator's IAM
root keys — not the descriptor's
keys/op.s3.keys, which is the aggregate's own S3 creds.
Drop them in operator_root.keys (or
wasabi_root.keys) in the config dir, or pass
--operator-root-keys-file; with root keys in place you pass
neither creds nor bucket.
accorder fedlib members invite fed alice --prefix alice
fedlib members offer mints a peer's claim URL:
accorder fedlib members offer fed --prefix bob --librarian bob --endpoint http://op:8800
fedlib build bootstraps the aggregate once; the
positional pulls bucket, S3, output path, and keys-file from the
descriptor, so it's credless too.
accorder fedlib build fed
Then fedlib share live serves it and rebuilds
automatically as members change:
accorder fedlib share live fed --only-http --poll-interval 5 --listen 0.0.0.0:8800
A restart replays the saved configuration; as with
lib share live, --only-http is per-run, so
repeat it on a Tor-enabled build:
accorder fedlib share live fed --only-http
accorder lib join)Each librarian joins with what the operator minted above —
alice with the operator-hosted membership blob
(accorder-fedlib-invite:…), bob with his
peer-offer claim URL. The alias is the first word; the blob or URL goes
to --invite.
Alice joins under the alias she has been building with all along.
That alias carries a library UUID she minted locally in step 1, and the
invite carries the federation's — so the join adopts the federation's
identity in its place. It can, because nothing was ever published under
the local one: the alias holds a catalog, not a membership. Her
calibrepath and librarian name are already on the alias, so the join
needs neither, and it builds and publishes into the bucket in one go (it
confirms first when run from a terminal). Later,
lib publish alice republishes after changes.
accorder lib join alice --invite 'accorder-fedlib-invite:…'
An alias that is already a membership — one carrying a federation prefix, a dict ID, a publish bucket or stored write credentials — refuses a conflicting invite instead, and says which of those it found. Re-inviting an existing member, where the invite carries the UUID the alias already holds, is not a conflict and simply refreshes the credentials. Refusals that can be decided from the alias and the offer's prefix happen before the claim, so a refused join leaves a single-use offer unspent.
bob — browsing into his own Calibre since step 1 — hosts
his library himself. First he builds it:
accorder lib build bob
Then he shares it, and the share keeps serving in its terminal:
accorder lib share live bob --only-http --listen 0.0.0.0:8900 --mode private
From a second terminal he claims the peer offer. The operator pulls
from his live share; his --libraryID comes from his
alias:
accorder lib join bob --invite 'http://op:8800/invite/…' --endpoint http://bob-host:8900 --insecure
Re-run lib build bob after changes — the operator's poll
picks it up.
A fedlib aggregate is itself a library — today you can
lib browse it from the top, render any member's books into
your Calibre, and re-share (step 1, one level up); what you rendered can
join a federation via lib join (step 2). Aggregates joining
other aggregates is the direction, not yet a command —
lib join operates on a librarian's Calibre library, not on
a fedlib. Books already move catalog → catalog the whole way:
accorder lib browse alice --endpoint https://<a-fedlib>