mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-11-08 04:22:06 +00:00
Migration 0028 made devices.secret_lookup (hex(sha256(secret))) the verifier for device authentication, but it could not be backfilled: the plaintext is not recoverable from the bcrypt hash. Rows created before it were left NULL, and ValidateDeviceSecret carried a fallback that scanned every such row with bcrypt.CompareHashAndPassword and then lazily wrote the lookup value in. That branch is the last way a single legacy row can put an O(number of devices) bcrypt scan back on /auth/token, which is the defect 0028 was written to remove. Production has no NULL-lookup rows left, so it is removed: the indexed lookup is now the only path, and a miss is an authentication failure. validateBySecretLookup is folded into ValidateDeviceSecret, since there is no longer a second path to fall through to. bcrypt is still written at device creation. secret_hash backs the UNIQUE constraint and keys UpdateLastUsed; it is simply never compared against on the authentication path. Migration 0036 deletes rows with a NULL or empty secret_lookup. They cannot authenticate once the fallback is gone, and leaving them would only misreport them as live devices in the UI and the MAU queries. The column stays nullable because SQLite cannot add NOT NULL to an existing column without a table rebuild, and a NULL now reads as a non-match either way. Tests: TestDeviceStore_LegacyDeviceBackfills is replaced by TestDeviceStore_NullSecretLookupDoesNotAuthenticate, which asserts a row whose secret_hash genuinely matches the presented secret is still rejected once its lookup value is cleared, and that nothing backfills it. test/e2e/seed-legacy- devices.go existed only to reproduce the legacy scan and is deleted. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012PD7KfLzE27F3qeCqDSwF3