mirror of
https://github.com/runbear-io/beardrive.git
synced 2026-08-25 08:08:08 +02:00
ownJournal refuses a journal write unless the device id is bound to the caller's account, for EVERY provider — it asks only whether s.Devices is nil. The only thing that creates that binding is DeviceRegistry.Bind, whose only caller is BuiltinAuth.finishLogin, and that hook was wired behind `if a, ok := s.Auth.(*BuiltinAuth); ok`. So a hub running a managed AuthProvider — the deployment the seam exists for — bound nothing, ever, and refused every journal push from every device forever. Everything around it read healthy: /api/auth/me answered, project permissions said write, and blobs (content-addressed, so ownerless) uploaded fine. Only the journal PUT died. Signing in again could not help, because signing in was the step that was supposed to bind. UseDeviceBinder moves the hook onto the AuthProvider interface — a breaking change for an out-of-tree provider, deliberately, so one that ignores a precondition of a gate the hub enforces for it does not compile. The hub cannot bind on the provider's behalf: a bind must be reachable only from a completed authentication, and Authenticate reports who a request is, never which credential class it presented. A device token still cannot reach a bind; no new door was added, and every /store/* door still creates nothing. Bind also reported success for a row the store had refused — observeLocked logged the write failure and swallowed it — so a login could hand back a token whose every push was then denied, with nothing in the hub explaining why. It now propagates and the login fails honestly. And a hub that refuses a journal write while holding no binding at all logs that its provider is not calling the binder — the sentence that would have ended this investigation on day one instead of day two. Tested by driving the real binary against a hub with a managed provider (cli_provider_e2e_test.go), which is the configuration no test in this repo covered and the reason this shipped: every existing test used BuiltinAuth, where the wiring happened to work. Both directions are pinned — a provider that binds pushes, one that ignores the binder reproduces the reported symptom exactly. Claude-Session: https://claude.ai/code/session_01GSHsQU4pBCzKkPyPeXSwTm Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>