Clone this repository
For self-hosted knots, clone URLs may differ based on your setup.
Download tar.gz
Critical: correct V006 migration comment — SQLite does not auto-update FK
references in child tables on RENAME; the migration is safe because all
tables are empty (no DML-time FK checks fire).
Important:
- Add UNIQUE INDEX idx_devices_token_hash on devices.device_token_hash
- Add max-length check (512 chars) on devicePublicKey input
- Add #[tracing::instrument] + claim_code field to redeem_and_register;
distinguish RowNotFound from other errors in log messages
- Fix seed_pending_account helper to generate unique codes/email/handle
per call so it is safe to invoke multiple times on the same pool
- Add orphaned_claim_code_returns_500_and_does_not_redeem_code test
(verifies atomicity: transaction rolls back if pending_accounts lookup
fails, leaving claim code unredeemed)
- Extend closed_db_pool_returns_500 and platform_is_case_sensitive tests
to assert error code in response body
- Add oversized_public_key_returns_400 test
- Add oversized_public_key_returns_400 test (boundary test for devicePublicKey, mirrors register_device.rs analogue)
- Add empty_email_returns_400 test (present-but-empty email returns 400, not 422)
- Document V007 pending_sessions migration in crates/relay/src/db/CLAUDE.md
Combined mobile account creation endpoint for the iOS identity wallet
onboarding flow. Atomically redeems a claim code, creates a pending
account, registers the device, and issues a pending session token in a
single transaction — with full rollback on any step failure.
- V007 migration: pending_sessions table (token_hash UNIQUE, FKs to
pending_accounts and devices) for pre-DID session tokens
- ClaimCodeRedeemed ErrorCode (409) to distinguish already-redeemed
codes from invalid/expired ones (404) per spec
- validate_handle and is_valid_platform promoted to pub(crate) for reuse
- Bruno collection entry for the new route
Critical: correct V006 migration comment — SQLite does not auto-update FK
references in child tables on RENAME; the migration is safe because all
tables are empty (no DML-time FK checks fire).
Important:
- Add UNIQUE INDEX idx_devices_token_hash on devices.device_token_hash
- Add max-length check (512 chars) on devicePublicKey input
- Add #[tracing::instrument] + claim_code field to redeem_and_register;
distinguish RowNotFound from other errors in log messages
- Fix seed_pending_account helper to generate unique codes/email/handle
per call so it is safe to invoke multiple times on the same pool
- Add orphaned_claim_code_returns_500_and_does_not_redeem_code test
(verifies atomicity: transaction rolls back if pending_accounts lookup
fails, leaving claim code unredeemed)
- Extend closed_db_pool_returns_500 and platform_is_case_sensitive tests
to assert error code in response body
- Add oversized_public_key_returns_400 test
Device registration via claim code: validates and redeems a single-use claim
code, stores the device public key, generates an opaque device_token (stored
as SHA-256 hash, returned once), and enforces platform validation.
V006 migration rebuilds the devices table to reference pending_accounts.id
instead of accounts.did (registration precedes DID assignment), adding
platform, public_key, and device_token_hash columns. sessions, oauth_tokens,
and refresh_tokens are also rebuilt to maintain correct FK targets after the
cascading rename.
Critical:
- Run cargo fmt --all (formatting violations in auth.rs, create_account.rs)
- Add unit tests for require_admin_token() in auth.rs (6 tests covering all
branches including the non-UTF-8 Authorization header path)
- Add unit tests for generate_code() in code_gen.rs (4 tests: length, charset,
character set membership, non-constant output)
Important:
- Narrow pub mod auth to pub(crate) mod auth in routes/mod.rs
- Drop pub from CODE_LEN and CHARSET in code_gen.rs (no external consumers)
- Switch OR EXISTS queries from bool to i64 + CAST AS INTEGER to avoid
sqlx type-affinity ambiguity on untyped SQLite expressions
- Narrow auth.rs doc comment: presence/prefix checks are conventional
short-circuits; only the final comparison uses subtle::ct_eq
- Remove stale "handle_in_handles query coverage" comment from test
- Log constraint name in unique_violation_source default arm so unexpected
future constraints are visible in traces
Suggestions (high-value):
- Use bool::from(ct_eq(...)) instead of unwrap_u8() != 1 per subtle docs
- Upgrade non-UTF-8 Authorization header log from debug to warn