Summary
Skaldleita-side log analysis shows 21,170 keyless 401 requests from 49 IPs over 14 days = 13.8% of all API traffic. These are Library Manager installs that registered, received an API key, but send requests without it — so every call 401s and the client retries forever (one IP: ~110 req/day, 100% 401, for 15+ days straight, zero backoff).
Cross-referencing the spam IPs to Skaldleita's registration DB: every offender already has a valid approved key on file. The bootstrap worked; the key just isn't being attached to outbound requests.
Root cause
The auto-inject is already correctly implemented on develop:
- Wire 1 — register:
app.py:10858 POSTs /api/register-instance
- Wire 2 — save:
app.py:10880 secrets['bookdb_api_key'] = result['api_key']
- Wire 3 — attach:
app.py:3089, app.py:11747 build headers['X-API-Key'] = secrets.get('bookdb_api_key') or BOOKDB_PUBLIC_KEY
So the spam is from old builds that predate this (betas 0.9.0-beta.134–.152 and main-branch builds sending a python-requests/* User-Agent, i.e. no LibraryManager/ UA at all).
Action items
1. Ship it — develop → main → release ⬅ biggest lever
The working auto-inject is on develop. The spamming users are on main/old betas. Merging develop→main and cutting a release is what actually gets the fix to them. (Same blocker noted previously for the wynter242 /search 401 loop.)
2. Patch 3 remaining keyless call sites (even develop still 401s these)
Each has a stale # Uses public endpoint - no API key required comment — true before Skaldleita required keys on all endpoints (2026-06-02), false now:
app.py:11909 — GET /book/{book_id} (book hover cards) → no headers
app.py:11983 — GET /author/{author_id} (author hover cards) → no headers
app.py:2299 — GET /search (ebook verification path) → no headers
Fix: attach auth the same way the working paths do:
secrets = load_secrets()
api_key = secrets.get('bookdb_api_key') or BOOKDB_PUBLIC_KEY
headers = get_signed_headers() or {}
headers['X-API-Key'] = api_key
# ... requests.get(url, headers=headers, ...)
3. (Optional) Register the librarymanager://setkey?key=... URL scheme
Skaldleita's key-delivery email now includes a one-tap deep link (skaldleita PR #154). If LM registers a handler for librarymanager://setkey?key=<key> that writes the (URL-decoded) key into secrets['bookdb_api_key'], users get one-tap key injection from the email as a fallback when startup auto-register doesn't fire.
4. (Recommended) Enforce the version floor
/health returns min_library_manager_version. Having LM check it and hard-nag/block below the floor would stop old builds from silently looping in the future.
Evidence
- 13.8% of Skaldleita traffic is keyless-401 spam (14-day window).
- Spam IPs map to real registered users (betas .134/.150/.151/.152) — all with keys on file.
- 401s are not rate-limited server-side, so broken clients loop indefinitely (Skaldleita is adding 401 backpressure separately).
Filed from a Skaldleita session; LM code referenced from the develop checkout.
Summary
Skaldleita-side log analysis shows 21,170 keyless
401requests from 49 IPs over 14 days = 13.8% of all API traffic. These are Library Manager installs that registered, received an API key, but send requests without it — so every call 401s and the client retries forever (one IP: ~110 req/day, 100% 401, for 15+ days straight, zero backoff).Cross-referencing the spam IPs to Skaldleita's registration DB: every offender already has a valid approved key on file. The bootstrap worked; the key just isn't being attached to outbound requests.
Root cause
The auto-inject is already correctly implemented on
develop:app.py:10858POSTs/api/register-instanceapp.py:10880secrets['bookdb_api_key'] = result['api_key']app.py:3089,app.py:11747buildheaders['X-API-Key'] = secrets.get('bookdb_api_key') or BOOKDB_PUBLIC_KEYSo the spam is from old builds that predate this (betas
0.9.0-beta.134–.152and main-branch builds sending apython-requests/*User-Agent, i.e. noLibraryManager/UA at all).Action items
1. Ship it —
develop→main→ release ⬅ biggest leverThe working auto-inject is on
develop. The spamming users are onmain/old betas. Mergingdevelop→mainand cutting a release is what actually gets the fix to them. (Same blocker noted previously for the wynter242/search401 loop.)2. Patch 3 remaining keyless call sites (even
developstill 401s these)Each has a stale
# Uses public endpoint - no API key requiredcomment — true before Skaldleita required keys on all endpoints (2026-06-02), false now:app.py:11909—GET /book/{book_id}(book hover cards) → no headersapp.py:11983—GET /author/{author_id}(author hover cards) → no headersapp.py:2299—GET /search(ebook verification path) → no headersFix: attach auth the same way the working paths do:
3. (Optional) Register the
librarymanager://setkey?key=...URL schemeSkaldleita's key-delivery email now includes a one-tap deep link (skaldleita PR #154). If LM registers a handler for
librarymanager://setkey?key=<key>that writes the (URL-decoded) key intosecrets['bookdb_api_key'], users get one-tap key injection from the email as a fallback when startup auto-register doesn't fire.4. (Recommended) Enforce the version floor
/healthreturnsmin_library_manager_version. Having LM check it and hard-nag/block below the floor would stop old builds from silently looping in the future.Evidence
Filed from a Skaldleita session; LM code referenced from the
developcheckout.