mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 11:46:35 +00:00
The broker built every handle as kc-<username>. The PDS (0.4.66) caps the
first handle label at 18 characters, so kc-kevinveenbirkenbach (22) failed
createAccount with InvalidHandle: Handle too long, and the prefix only
existed to dodge the PDS's reserved names.
handle.js derives the label from the Keycloak username without a prefix and
adds i8- only after the PDS answers Reserved handle (1029 names, among them
administrator). A label that fits neither way leads to a form at /sso/handle
where the user picks 3 to 18 characters of [a-z0-9-] that contain a hyphen,
which no Keycloak username holds, and never contain i8-. The POST must come
from the proxied host's origin; the hostname is compared, since nginx passes
Host without its port.
store.js keeps username -> {handle, did, encrypted app-password} in a JSON
file (0600, written through a rename) in the new broker_data volume, so a
broker restart neither creates a second account nor resets a password. A
stored entry whose login the PDS refuses gets its password reset by DID; when
that login still fails the account is gone (a single-volume PDS rollback) and
the user is provisioned again. Concurrent first visits share one
provisioning. A derived handle that is taken is only recovered when no other
user holds it in the store, and an e-mail address a legacy kc- account still
holds ends in a 409 page instead of a retry loop.
Without the broker, 04_native_admin.yml follows the same rule: the plain
handle first, i8- on Reserved handle. The native Playwright tests sign in by
e-mail, which the PDS resolves, and accept either handle.
Evidence: the PDS rules and messages were read from the 0.4.66 image.
server.test.js runs the broker as a child process against a PDS stub that
answers with those messages: plain, i8-, form, 403/400/409, the takeover
guard, restart, lost account, reset and the concurrent visits, whose test
fails without provisionOnce. Unit tests 21/21, JavaScript suite 102/102,
make test-lint and make lint pass. The image builds and the node user writes
the store into a fresh volume.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>