recall-server 0.4.14

Recall's sync server: SQLite persistence, LLM-assisted merge, and the HTTP API
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
//! The owner's passkeys, the admin page's sessions, and the one-time code
//! that registers the first passkey.
//!
//! A passkey is stored as `webauthn-rs` serialises it, in `passkey`, and
//! that JSON is opaque to everything here: the store never needs to look
//! inside it, so it does not depend on the crate that wrote it. What the
//! store does need to compare, the credential id, the user handle and the
//! signature counter, has a column of its own.
//!
//! Timestamps are [`crate::now`]'s format, so they compare as strings.

use anyhow::Result;
use rusqlite::{Connection, OptionalExtension, Row};

use super::{Outcome, Store};

/// Created alongside the other tables, every time the store opens.
pub(super) const SCHEMA: &str = "
    CREATE TABLE IF NOT EXISTS admin_credentials (
        -- The WebAuthn credential id, base64url without padding.
        id           TEXT PRIMARY KEY,
        -- The WebAuthn user handle, a UUID. Every passkey has the same one:
        -- there is one owner, and an authenticator given the same handle
        -- twice for a site replaces its passkey rather than adding one.
        user_handle  TEXT NOT NULL,
        -- What the owner called it, such as 'iPhone'.
        name         TEXT NOT NULL,
        -- webauthn-rs's Passkey, as JSON: the public key and its flags.
        passkey      TEXT NOT NULL,
        -- The authenticator's signature counter at the last sign-in. Kept
        -- beside the JSON so that checking it and moving it forward is one
        -- conditional UPDATE, which two sign-ins at once cannot both pass.
        sign_count   INTEGER NOT NULL DEFAULT 0,
        created_at   TEXT NOT NULL,
        last_used_at TEXT
    );
    CREATE TABLE IF NOT EXISTS admin_sessions (
        -- SHA-256 of the cookie's value, lowercase hex. The value itself is
        -- never stored, so a copy of the database signs nobody in.
        token_sha256  TEXT PRIMARY KEY,
        -- The passkey that signed in: removing it ends its sessions.
        credential_id TEXT NOT NULL,
        created_at    TEXT NOT NULL,
        last_used_at  TEXT NOT NULL,
        -- The absolute limit, however recently it was used.
        expires_at    TEXT NOT NULL
    );
    CREATE TABLE IF NOT EXISTS admin_bootstrap (
        -- One row at most: a new code replaces the last.
        id          INTEGER PRIMARY KEY CHECK (id = 1),
        -- SHA-256 of the code, lowercase hex, as `bootstrap_code` reads
        -- it. The code itself is never stored.
        code_sha256 TEXT NOT NULL,
        created_at  TEXT NOT NULL,
        -- Registering the first passkey with it must happen before this.
        expires_at  TEXT NOT NULL
    );
";

/// One of the owner's passkeys, as the store holds it.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AdminCredential {
    /// The credential id, base64url.
    pub id: String,
    /// The WebAuthn user handle.
    pub user_handle: String,
    /// What the owner called it.
    pub name: String,
    /// webauthn-rs's `Passkey`, as JSON.
    pub passkey: String,
    /// The signature counter at the last sign-in.
    pub sign_count: u32,
    /// When it was registered.
    pub created_at: String,
    /// When it last signed in, if ever.
    pub last_used_at: Option<String>,
}

/// A passkey about to be stored.
#[derive(Debug, Clone)]
pub struct NewAdminCredential<'a> {
    /// The credential id, base64url.
    pub id: &'a str,
    /// The WebAuthn user handle.
    pub user_handle: &'a str,
    /// What the owner called it.
    pub name: &'a str,
    /// webauthn-rs's `Passkey`, as JSON.
    pub passkey: &'a str,
    /// Its signature counter as registered.
    pub sign_count: u32,
    /// Now.
    pub created_at: &'a str,
}

/// The bootstrap code a first passkey is being registered with: the hash
/// of what was given, and the moment it is judged at.
#[derive(Debug, Clone, Copy)]
pub struct FirstPasskey<'a> {
    /// SHA-256 of the code, as `bootstrap_code` reads it.
    pub code_sha256: &'a str,
    /// Now.
    pub now: &'a str,
}

/// What storing a passkey came to.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AddedCredential {
    /// Stored.
    Added,
    /// Only the first passkey may be stored this way, and there is one
    /// already.
    NotFirst,
    /// A passkey with that credential id is stored already.
    Duplicate,
    /// The bootstrap code is not the one outstanding, or has expired.
    Code(BootstrapCode),
}

/// Whether a bootstrap code will register a first passkey.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BootstrapCode {
    /// It is the one outstanding, and has not expired.
    Valid,
    /// It was, but it has expired.
    Expired,
    /// It is not one this server issued, or was replaced or used already.
    Wrong,
}

/// What removing a passkey came to.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RemovedCredential {
    /// Removed, with every session it signed in.
    Removed(AdminCredential),
    /// It is the only one; removing it would lock the owner out.
    Last,
    /// No passkey has that id.
    NotFound,
}

/// An admin session, as the store holds it.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AdminSession {
    /// The passkey that signed in.
    pub credential_id: String,
    /// When it signed in.
    pub created_at: String,
    /// When it was last used.
    pub last_used_at: String,
    /// When it ends, however recently it was used.
    pub expires_at: String,
}

const CREDENTIAL_COLUMNS: &str =
    "id, user_handle, name, passkey, sign_count, created_at, last_used_at";

fn credential_from(r: &Row<'_>) -> rusqlite::Result<AdminCredential> {
    Ok(AdminCredential {
        id: r.get(0)?,
        user_handle: r.get(1)?,
        name: r.get(2)?,
        passkey: r.get(3)?,
        sign_count: r.get(4)?,
        created_at: r.get(5)?,
        last_used_at: r.get(6)?,
    })
}

fn get_credential(conn: &Connection, id: &str) -> Result<Option<AdminCredential>> {
    Ok(conn
        .query_row(
            &format!("SELECT {CREDENTIAL_COLUMNS} FROM admin_credentials WHERE id = ?1"),
            (id,),
            credential_from,
        )
        .optional()?)
}

fn bootstrap_code(conn: &Connection, code_sha256: &str, now: &str) -> Result<BootstrapCode> {
    let expires_at: Option<String> = conn
        .query_row(
            "SELECT expires_at FROM admin_bootstrap WHERE code_sha256 = ?1",
            (code_sha256,),
            |r| r.get(0),
        )
        .optional()?;
    Ok(match expires_at {
        Some(expires_at) if expires_at.as_str() > now => BootstrapCode::Valid,
        Some(_) => BootstrapCode::Expired,
        None => BootstrapCode::Wrong,
    })
}

fn set_bootstrap_code(
    conn: &Connection,
    code_sha256: &str,
    now: &str,
    expires_at: &str,
) -> Result<()> {
    conn.execute(
        "INSERT OR REPLACE INTO admin_bootstrap (id, code_sha256, created_at, expires_at)
         VALUES (1, ?1, ?2, ?3)",
        (code_sha256, now, expires_at),
    )?;
    Ok(())
}

impl Store {
    /// Whether the owner has registered any passkey.
    pub fn has_admin_credentials(&self) -> Result<bool> {
        let conn = self.lock();
        let n: i64 = conn.query_row("SELECT COUNT(*) FROM admin_credentials", [], |r| r.get(0))?;
        Ok(n > 0)
    }

    /// Stores a passkey, with the `passkey_add` leaf `build_leaf` makes, in
    /// one transaction.
    ///
    /// With `first`, only while none is stored, and only with the bootstrap
    /// code outstanding, which it uses up. All three are one transaction,
    /// so two bootstraps at once cannot both be the first, nor one code
    /// register two passkeys. It takes the write lock before anything is
    /// read, as every audited write does: `reset-passkeys` writes from
    /// another process.
    pub fn add_admin_credential_audited(
        &self,
        c: &NewAdminCredential<'_>,
        first: Option<FirstPasskey<'_>>,
        build_leaf: impl FnOnce(u64, &str) -> Vec<u8>,
    ) -> Result<AddedCredential> {
        self.audited(
            |tx, _| {
                if get_credential(tx, c.id)?.is_some() {
                    return Ok(Outcome::Refuse(AddedCredential::Duplicate));
                }
                if let Some(first) = first {
                    let n: i64 =
                        tx.query_row("SELECT COUNT(*) FROM admin_credentials", [], |r| r.get(0))?;
                    if n > 0 {
                        return Ok(Outcome::Refuse(AddedCredential::NotFirst));
                    }
                    match bootstrap_code(tx, first.code_sha256, first.now)? {
                        BootstrapCode::Valid => {}
                        refused => return Ok(Outcome::Refuse(AddedCredential::Code(refused))),
                    }
                    tx.execute("DELETE FROM admin_bootstrap", [])?;
                }
                tx.execute(
                    "INSERT INTO admin_credentials
                         (id, user_handle, name, passkey, sign_count, created_at)
                     VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
                    (
                        c.id,
                        c.user_handle,
                        c.name,
                        c.passkey,
                        c.sign_count,
                        c.created_at,
                    ),
                )?;
                Ok(Outcome::Commit(AddedCredential::Added))
            },
            |seq, at, _| build_leaf(seq, at),
        )
    }

    /// Whether `code_sha256` is the bootstrap code outstanding at `now`.
    pub fn check_bootstrap_code(&self, code_sha256: &str, now: &str) -> Result<BootstrapCode> {
        bootstrap_code(&self.lock(), code_sha256, now)
    }

    /// Makes `code_sha256` the one bootstrap code, until `expires_at`,
    /// replacing any other, with the `bootstrap_code` leaf `build_leaf`
    /// makes.
    pub fn set_bootstrap_code_audited(
        &self,
        code_sha256: &str,
        now: &str,
        expires_at: &str,
        build_leaf: impl FnOnce(u64, &str) -> Vec<u8>,
    ) -> Result<()> {
        self.audited(
            |tx, _| {
                set_bootstrap_code(tx, code_sha256, now, expires_at)?;
                Ok(Outcome::Commit(()))
            },
            |seq, at, ()| build_leaf(seq, at),
        )
    }

    /// One passkey.
    pub fn admin_credential(&self, id: &str) -> Result<Option<AdminCredential>> {
        get_credential(&self.lock(), id)
    }

    /// Every passkey, oldest first.
    pub fn admin_credentials(&self) -> Result<Vec<AdminCredential>> {
        let conn = self.lock();
        let mut stmt = conn.prepare(&format!(
            "SELECT {CREDENTIAL_COLUMNS} FROM admin_credentials ORDER BY created_at, id"
        ))?;
        let rows = stmt.query_map([], credential_from)?;
        Ok(rows.collect::<rusqlite::Result<_>>()?)
    }

    /// Records a sign-in, if its signature counter moved forward.
    ///
    /// WebAuthn §7.2 step 22: when either the stored counter or the new one
    /// is nonzero, the new one must be greater, or two copies of the
    /// credential's key may exist. Both zero is what a synced passkey
    /// reports every time, and is accepted. The check and the update are one
    /// statement, so two sign-ins racing with the same counter cannot both
    /// pass. Answers whether it was recorded; `false` is a refusal.
    pub fn record_admin_sign_in(
        &self,
        id: &str,
        sign_count: u32,
        passkey: &str,
        now: &str,
    ) -> Result<bool> {
        let conn = self.lock();
        let updated = conn.execute(
            "UPDATE admin_credentials
             SET sign_count = ?2, passkey = ?3, last_used_at = ?4
             WHERE id = ?1 AND (sign_count < ?2 OR (sign_count = 0 AND ?2 = 0))",
            (id, sign_count, passkey, now),
        )?;
        Ok(updated == 1)
    }

    /// Removes a passkey and every session it signed in, unless it is the
    /// last one, with the `passkey_remove` leaf `build_leaf` makes from it.
    pub fn remove_admin_credential_audited(
        &self,
        id: &str,
        build_leaf: impl FnOnce(u64, &str, &AdminCredential) -> Vec<u8>,
    ) -> Result<RemovedCredential> {
        self.audited(
            |tx, _| {
                let Some(credential) = get_credential(tx, id)? else {
                    return Ok(Outcome::Refuse(RemovedCredential::NotFound));
                };
                let n: i64 =
                    tx.query_row("SELECT COUNT(*) FROM admin_credentials", [], |r| r.get(0))?;
                if n <= 1 {
                    return Ok(Outcome::Refuse(RemovedCredential::Last));
                }
                tx.execute("DELETE FROM admin_credentials WHERE id = ?1", (id,))?;
                tx.execute("DELETE FROM admin_sessions WHERE credential_id = ?1", (id,))?;
                Ok(Outcome::Commit(RemovedCredential::Removed(credential)))
            },
            |seq, at, removed| match removed {
                RemovedCredential::Removed(credential) => build_leaf(seq, at, credential),
                _ => unreachable!("a leaf only for a removal"),
            },
        )
    }

    /// Removes every passkey and every session, and makes `code_sha256`
    /// the bootstrap code until `expires_at`: what `recall-server
    /// reset-passkeys` does, for an owner who has lost them all. One
    /// transaction with the `passkey_reset` leaf `build_leaf` makes from how
    /// many passkeys went, which it answers, and it creates the tables
    /// first if this database has never been opened by a server that has
    /// them.
    ///
    /// Run from another process than the server's, on the same file: the
    /// leaf takes the next `seq` the table has, and a running server reads
    /// it into its tree before its own next append (see
    /// [`Store::audited_each`]).
    pub fn reset_admin_credentials_audited(
        &self,
        code_sha256: &str,
        now: &str,
        expires_at: &str,
        build_leaf: impl FnOnce(u64, &str, usize) -> Vec<u8>,
    ) -> Result<usize> {
        self.lock().execute_batch(super::audit::SCHEMA)?;
        self.audited(
            |tx, _| {
                tx.execute_batch(SCHEMA)?;
                let n = tx.execute("DELETE FROM admin_credentials", [])?;
                tx.execute("DELETE FROM admin_sessions", [])?;
                set_bootstrap_code(tx, code_sha256, now, expires_at)?;
                Ok(Outcome::Commit(n))
            },
            |seq, at, n| build_leaf(seq, at, *n),
        )
    }

    /// Stores a new session, only if the passkey that signed it in is still
    /// there: a sign-in that finishes as its passkey is removed must not
    /// outlive it.
    pub fn create_admin_session(
        &self,
        token_sha256: &str,
        credential_id: &str,
        now: &str,
        expires_at: &str,
    ) -> Result<bool> {
        let conn = self.lock();
        let inserted = conn.execute(
            "INSERT INTO admin_sessions
                 (token_sha256, credential_id, created_at, last_used_at, expires_at)
             SELECT ?1, ?2, ?3, ?3, ?4
             WHERE EXISTS (SELECT 1 FROM admin_credentials WHERE id = ?2)",
            (token_sha256, credential_id, now, expires_at),
        )?;
        Ok(inserted == 1)
    }

    /// One session, by the hash of its token.
    pub fn admin_session(&self, token_sha256: &str) -> Result<Option<AdminSession>> {
        let conn = self.lock();
        Ok(conn
            .query_row(
                "SELECT credential_id, created_at, last_used_at, expires_at
                 FROM admin_sessions WHERE token_sha256 = ?1",
                (token_sha256,),
                |r| {
                    Ok(AdminSession {
                        credential_id: r.get(0)?,
                        created_at: r.get(1)?,
                        last_used_at: r.get(2)?,
                        expires_at: r.get(3)?,
                    })
                },
            )
            .optional()?)
    }

    /// Records that a session was used, which is what keeps it from going
    /// idle.
    pub fn touch_admin_session(&self, token_sha256: &str, now: &str) -> Result<()> {
        let conn = self.lock();
        conn.execute(
            "UPDATE admin_sessions SET last_used_at = ?2 WHERE token_sha256 = ?1",
            (token_sha256, now),
        )?;
        Ok(())
    }

    /// Ends every session but `keep`, with the `sessions_end` leaf
    /// `build_leaf` makes from how many ended, which it answers. Ending none
    /// changes nothing and appends nothing.
    pub fn delete_other_admin_sessions_audited(
        &self,
        keep: &str,
        build_leaf: impl FnOnce(u64, &str, usize) -> Vec<u8>,
    ) -> Result<usize> {
        self.audited(
            |tx, _| {
                let n = tx.execute(
                    "DELETE FROM admin_sessions WHERE token_sha256 != ?1",
                    (keep,),
                )?;
                Ok(if n == 0 {
                    Outcome::Refuse(0)
                } else {
                    Outcome::Commit(n)
                })
            },
            |seq, at, n| build_leaf(seq, at, *n),
        )
    }

    /// Ends a session.
    pub fn delete_admin_session(&self, token_sha256: &str) -> Result<()> {
        let conn = self.lock();
        conn.execute(
            "DELETE FROM admin_sessions WHERE token_sha256 = ?1",
            (token_sha256,),
        )?;
        Ok(())
    }

    /// Removes sessions past their absolute limit, or idle since before
    /// `idle_before`. Answers how many went.
    pub fn sweep_admin_sessions(&self, now: &str, idle_before: &str) -> Result<usize> {
        let conn = self.lock();
        Ok(conn.execute(
            "DELETE FROM admin_sessions WHERE expires_at <= ?1 OR last_used_at < ?2",
            (now, idle_before),
        )?)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::store::test_leaf;

    /// The audited writes, with a leaf these tests do not look at, under
    /// the names the tests read best with.
    impl Store {
        fn add_admin_credential(
            &self,
            c: &NewAdminCredential<'_>,
            first: Option<FirstPasskey<'_>>,
        ) -> Result<AddedCredential> {
            self.add_admin_credential_audited(c, first, test_leaf)
        }

        fn set_bootstrap_code(&self, code_sha256: &str, now: &str, expires_at: &str) -> Result<()> {
            self.set_bootstrap_code_audited(code_sha256, now, expires_at, test_leaf)
        }

        fn remove_admin_credential(&self, id: &str) -> Result<RemovedCredential> {
            self.remove_admin_credential_audited(id, |seq, at, _| test_leaf(seq, at))
        }

        fn reset_admin_credentials(
            &self,
            code_sha256: &str,
            now: &str,
            expires_at: &str,
        ) -> Result<usize> {
            self.reset_admin_credentials_audited(code_sha256, now, expires_at, |seq, at, _| {
                test_leaf(seq, at)
            })
        }

        fn delete_other_admin_sessions(&self, keep: &str) -> Result<usize> {
            self.delete_other_admin_sessions_audited(keep, |seq, at, _| test_leaf(seq, at))
        }
    }

    fn credential<'a>(id: &'a str, created_at: &'a str) -> NewAdminCredential<'a> {
        NewAdminCredential {
            id,
            user_handle: "u",
            name: "phone",
            passkey: "{}",
            sign_count: 0,
            created_at,
        }
    }

    const T0: &str = "2026-09-23T10:00:00.000Z";
    const T1: &str = "2026-09-23T11:00:00.000Z";
    const T2: &str = "2026-09-23T12:00:00.000Z";

    fn first(code_sha256: &str) -> Option<FirstPasskey<'_>> {
        Some(FirstPasskey {
            code_sha256,
            now: T0,
        })
    }

    /// A store with the bootstrap code "code" outstanding until T1.
    fn bootstrapping() -> Store {
        let st = Store::open_in_memory().unwrap();
        st.set_bootstrap_code("code", T0, T1).unwrap();
        st
    }

    #[test]
    fn only_the_first_passkey_can_be_added_as_the_first() {
        let st = bootstrapping();
        assert!(!st.has_admin_credentials().unwrap());
        assert_eq!(
            st.add_admin_credential(&credential("a", T0), first("code"))
                .unwrap(),
            AddedCredential::Added
        );
        assert!(st.has_admin_credentials().unwrap());
        st.set_bootstrap_code("again", T0, T1).unwrap();
        assert_eq!(
            st.add_admin_credential(&credential("b", T0), first("again"))
                .unwrap(),
            AddedCredential::NotFirst
        );
        assert_eq!(
            st.add_admin_credential(&credential("a", T0), None).unwrap(),
            AddedCredential::Duplicate
        );
        assert_eq!(
            st.add_admin_credential(&credential("b", T1), None).unwrap(),
            AddedCredential::Added
        );
        let ids: Vec<String> = st
            .admin_credentials()
            .unwrap()
            .into_iter()
            .map(|c| c.id)
            .collect();
        assert_eq!(ids, ["a", "b"]);
    }

    /// The counter rule, including the case webauthn-rs's own check cannot
    /// see: two sign-ins verified against the same stored counter, of which
    /// only the first may be recorded.
    /// Finding 3: the first passkey needs the code outstanding, before it
    /// expires, and uses it up.
    #[test]
    fn the_first_passkey_needs_the_bootstrap_code_once() {
        let st = bootstrapping();
        assert_eq!(
            st.check_bootstrap_code("code", T0).unwrap(),
            BootstrapCode::Valid
        );
        assert_eq!(
            st.check_bootstrap_code("code", T1).unwrap(),
            BootstrapCode::Expired
        );
        assert_eq!(
            st.check_bootstrap_code("other", T0).unwrap(),
            BootstrapCode::Wrong
        );
        assert_eq!(
            st.add_admin_credential(&credential("a", T0), first("other"))
                .unwrap(),
            AddedCredential::Code(BootstrapCode::Wrong)
        );
        let late = Some(FirstPasskey {
            code_sha256: "code",
            now: T1,
        });
        assert_eq!(
            st.add_admin_credential(&credential("a", T0), late).unwrap(),
            AddedCredential::Code(BootstrapCode::Expired)
        );
        assert!(!st.has_admin_credentials().unwrap(), "nothing stored");
        assert_eq!(
            st.add_admin_credential(&credential("a", T0), first("code"))
                .unwrap(),
            AddedCredential::Added
        );
        assert_eq!(
            st.check_bootstrap_code("code", T0).unwrap(),
            BootstrapCode::Wrong,
            "used up"
        );

        // Reset clears the passkey and makes a new code the only one.
        assert_eq!(st.reset_admin_credentials("new", T0, T2).unwrap(), 1);
        assert_eq!(
            st.check_bootstrap_code("new", T1).unwrap(),
            BootstrapCode::Valid
        );
        st.set_bootstrap_code("newer", T0, T2).unwrap();
        assert_eq!(
            st.check_bootstrap_code("new", T1).unwrap(),
            BootstrapCode::Wrong,
            "replaced"
        );
    }

    #[test]
    fn a_sign_in_is_recorded_only_if_its_counter_moved_forward() {
        let st = bootstrapping();
        st.add_admin_credential(&credential("a", T0), first("code"))
            .unwrap();
        // Both zero: a synced passkey, every time.
        assert!(st.record_admin_sign_in("a", 0, "{}", T1).unwrap());
        assert!(st.record_admin_sign_in("a", 0, "{}", T1).unwrap());
        assert!(st.record_admin_sign_in("a", 5, "{}", T1).unwrap());
        assert!(!st.record_admin_sign_in("a", 5, "{}", T1).unwrap(), "equal");
        assert!(!st.record_admin_sign_in("a", 4, "{}", T1).unwrap(), "lower");
        assert!(!st.record_admin_sign_in("a", 0, "{}", T1).unwrap(), "reset");
        assert!(st.record_admin_sign_in("a", 6, "{}", T1).unwrap());
        let stored = st.admin_credential("a").unwrap().unwrap();
        assert_eq!(stored.sign_count, 6);
        assert_eq!(stored.last_used_at.as_deref(), Some(T1));
        assert!(!st.record_admin_sign_in("nobody", 9, "{}", T1).unwrap());
    }

    #[test]
    fn the_last_passkey_cannot_be_removed_and_removing_one_ends_its_sessions() {
        let st = bootstrapping();
        st.add_admin_credential(&credential("a", T0), first("code"))
            .unwrap();
        assert_eq!(
            st.remove_admin_credential("a").unwrap(),
            RemovedCredential::Last
        );
        st.add_admin_credential(&credential("b", T1), None).unwrap();
        assert!(st.create_admin_session("s1", "a", T1, T1).unwrap());
        assert!(st.create_admin_session("s2", "b", T1, T1).unwrap());
        assert!(matches!(
            st.remove_admin_credential("a").unwrap(),
            RemovedCredential::Removed(c) if c.id == "a"
        ));
        assert_eq!(st.admin_session("s1").unwrap(), None);
        assert!(st.admin_session("s2").unwrap().is_some());
        assert_eq!(
            st.remove_admin_credential("a").unwrap(),
            RemovedCredential::NotFound
        );
        assert!(
            !st.create_admin_session("s3", "a", T1, T1).unwrap(),
            "a removed passkey signs nobody in"
        );
    }

    #[test]
    fn signing_out_the_others_keeps_this_session() {
        let st = bootstrapping();
        st.add_admin_credential(&credential("a", T0), first("code"))
            .unwrap();
        for s in ["mine", "theirs", "old"] {
            st.create_admin_session(s, "a", T0, T2).unwrap();
        }
        assert_eq!(st.delete_other_admin_sessions("mine").unwrap(), 2);
        assert!(st.admin_session("mine").unwrap().is_some());
        assert_eq!(st.admin_session("theirs").unwrap(), None);
        assert_eq!(st.delete_other_admin_sessions("mine").unwrap(), 0);
    }

    #[test]
    fn sessions_are_swept_when_idle_or_past_their_limit() {
        let st = bootstrapping();
        st.add_admin_credential(&credential("a", T0), first("code"))
            .unwrap();
        st.create_admin_session("idle", "a", T0, "2026-10-23T10:00:00.000Z")
            .unwrap();
        st.create_admin_session("old", "a", T1, T1).unwrap();
        st.create_admin_session("fine", "a", T1, "2026-10-23T10:00:00.000Z")
            .unwrap();
        assert_eq!(
            st.sweep_admin_sessions(T1, "2026-09-23T10:30:00.000Z")
                .unwrap(),
            2
        );
        assert!(st.admin_session("fine").unwrap().is_some());
        assert_eq!(st.reset_admin_credentials("new", T0, T2).unwrap(), 1);
        assert_eq!(st.admin_session("fine").unwrap(), None);
        assert!(!st.has_admin_credentials().unwrap());
    }
}