PERF: App Password verification costs 0.4–5.5 s per DAV request under load (cache verified credentials, revoke-aware) #512

Open
opened 2026-09-30 11:01:30 +00:00 by kayg · 19 comments
Owner

Found by #500 (2026-09-30)

Under concurrent DAV load, App Password verification took 0.4–5.5 s per request, and SQLx pool acquisition about 2.3–2.5 s. Every CalDAV/CardDAV/WebDAV/IMAP request re-verifies the App Password, probably with a full slow password hash each time, so many syncing devices multiply the cost.

Fix

  • A short-lived verified-credential cache keyed by a keyed hash of the credential (never the secret itself), invalidated immediately on revoke, on scope change and on User state change (the #472 and #483 checks must still hold).
  • Size the SQLx pool and keep the verification off the pool hot path.
  • Measure p50/p95 DAV request latency with 50 clients before and after on the perf VM (flock /root/perf.lock); share the cache with the MCP work (#492) so there is one implementation.
## Found by #500 (2026-09-30) Under concurrent DAV load, App Password verification took 0.4–5.5 s per request, and SQLx pool acquisition about 2.3–2.5 s. Every CalDAV/CardDAV/WebDAV/IMAP request re-verifies the App Password, probably with a full slow password hash each time, so many syncing devices multiply the cost. ## Fix - A short-lived verified-credential cache keyed by a keyed hash of the credential (never the secret itself), invalidated immediately on revoke, on scope change and on User state change (the #472 and #483 checks must still hold). - Size the SQLx pool and keep the verification off the pool hot path. - Measure p50/p95 DAV request latency with 50 clients before and after on the perf VM (flock /root/perf.lock); share the cache with the MCP work (#492) so there is one implementation.
Author
Owner

Starting #512 on job/apw-cache-512 from base SHA 558457cf32. I am locating the existing App Password verifier and designing the shared cache in the auth path so DAV and MCP use one implementation.

Starting #512 on job/apw-cache-512 from base SHA 558457cf32e1d429da3834a05ff2720d11284302. I am locating the existing App Password verifier and designing the shared cache in the auth path so DAV and MCP use one implementation.
Author
Owner

Finding: SqliteAuthStore::verify_app_password acquires the four-permit Argon gate, then selects the credential with fetch_optional(&self.pool). The server builds this store with from_pools(db.writer_pool(), db.reader_pool()), so every uncached DAV verification reads through the single-connection write pool before it starts Argon2. I will move verification reads to the reader pool and add one bounded, process-keyed positive cache inside SqliteAuthStore, shared by DAV and MCP.

Finding: `SqliteAuthStore::verify_app_password` acquires the four-permit Argon gate, then selects the credential with `fetch_optional(&self.pool)`. The server builds this store with `from_pools(db.writer_pool(), db.reader_pool())`, so every uncached DAV verification reads through the single-connection write pool before it starts Argon2. I will move verification reads to the reader pool and add one bounded, process-keyed positive cache inside `SqliteAuthStore`, shared by DAV and MCP.
Author
Owner

Branch sync: git fetch origin && git merge origin/dev completed as a fast-forward from 558457cf to cc25c441. The worktree was clean and there were no merge conflicts. I re-read the current auth decisions (§21) and kept the implementation in calternal-auth, shared by DAV and MCP.

Branch sync: `git fetch origin && git merge origin/dev` completed as a fast-forward from 558457cf to cc25c441. The worktree was clean and there were no merge conflicts. I re-read the current auth decisions (§21) and kept the implementation in `calternal-auth`, shared by DAV and MCP.
Author
Owner

Core implementation is ready. cargo clippy and cargo test pass for calternal-auth and calternal-db; cargo clippy -p calternal-server --all-targets -- -D warnings passes after building the SPA required by RustEmbed. The server test target is still compiling. The cache uses a process-keyed BLAKE3 credential digest, 30-second TTL, 4,096-entry bound and mutation generations; the App Password SELECT now uses the read pool.

Core implementation is ready. `cargo clippy` and `cargo test` pass for `calternal-auth` and `calternal-db`; `cargo clippy -p calternal-server --all-targets -- -D warnings` passes after building the SPA required by RustEmbed. The server test target is still compiling. The cache uses a process-keyed BLAKE3 credential digest, 30-second TTL, 4,096-entry bound and mutation generations; the App Password SELECT now uses the read pool.
Author
Owner

Finding from the #512 implementation: the positive cache alone allowed simultaneous DAV misses for the same credential to all reach Argon2 before any request inserted a result. This cold-start stampede was visible in verify_app_password: a cache miss proceeded directly to the reader query and bounded password verifier. I added per-key single-flight locks with a bounded overflow lock, plus a concurrency regression test. Auth crate gates are running now.

Finding from the #512 implementation: the positive cache alone allowed simultaneous DAV misses for the same credential to all reach Argon2 before any request inserted a result. This cold-start stampede was visible in `verify_app_password`: a cache miss proceeded directly to the reader query and bounded password verifier. I added per-key single-flight locks with a bounded overflow lock, plus a concurrency regression test. Auth crate gates are running now.
Author
Owner

Finding from the local 50-client DAV baseline: with 5 PROPFINDs per client, the pre-cache build returned 144 HTTP 429 responses out of 250 in the steady burst; all other responses were 207. The verifier waits at the four-slot Argon2 semaphore for up to five seconds, so valid requests under concurrent load can be rejected before completing. I updated the benchmark to retain 429 latency/status counts for the before run and to fail if the after run rate-limits a valid request.

Finding from the local 50-client DAV baseline: with 5 PROPFINDs per client, the pre-cache build returned 144 HTTP 429 responses out of 250 in the steady burst; all other responses were 207. The verifier waits at the four-slot Argon2 semaphore for up to five seconds, so valid requests under concurrent load can be rejected before completing. I updated the benchmark to retain 429 latency/status counts for the before run and to fail if the after run rate-limits a valid request.
Author
Owner

Issue #512 complete.

Branch: job/apw-cache-512
Base: cc25c441b7a974185622a1dee853cf38686d2b67
HEAD: 0b97202885034f4b199200eb7e698ad690f3e645

Built one App Password verification cache in calternal-auth, shared by the Basic DAV and bearer App Password paths used by DAV/MCP. It uses a per-process keyed BLAKE3 digest, positive entries with a 30-second maximum TTL, immediate generation invalidation on credential/User mutations, and per-credential single-flight verification. Argon2 and App Password/User lookups stay off the single-writer pool. The SQLite read pool default is 64.

The benchmark and raw data are in bench/app-password-dav.py and docs/perf/runs/2026-10-01-issue-512-app-password-dav.md. The perf VM lock was busy, so this was a local Linux debug-binary comparison under high host load. With 50 clients × 5 PROPFIND requests, warm p95 fell from 6,241 ms to 315 ms; cold-start p95 fell from 5,995 ms to 856 ms. The before run returned 429 for 155/250 warm requests and 125/250 cold requests. The after run returned 207 for all requests. tests/adversarial/webdav.py passed, and the next DAV request after revocation returned 401.

Decisions not stated in DESIGN: use a 30-second cache TTL and a 4,096-entry cap to bound staleness and memory; use 64 read connections for 50 concurrent DAV clients plus background reads; use one shared overflow lock if more than 4,096 unique verifications are in flight. The writer pool remains single-connection.

Files changed: crates/calternal-auth/Cargo.toml, crates/calternal-auth/src/store.rs, crates/calternal-db/src/db.rs, crates/calternal-server/src/wire.rs, bench/app-password-dav.py, docs/perf/README.md, and the three docs/perf/runs/2026-10-01-issue-512-app-password-dav* result files.

Gate output (verbatim):

cargo fmt --all --check
(exit 0; no stdout)

cargo clippy -p calternal-auth --all-targets -- -D warnings
    Checking calternal-auth v0.1.0 (/home/kayg/Developer/calternal-wt/apw-cache-512/crates/calternal-auth)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1m 08s

cargo test -p calternal-auth
test result: ok. 67 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 57.25s
Doc-tests calternal_auth
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

cargo clippy -p calternal-db --all-targets -- -D warnings
    Checking calternal-db v0.1.0 (/home/kayg/Developer/calternal-wt/apw-cache-512/crates/calternal-db)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 32.81s

cargo test -p calternal-db
test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.70s
Running tests/queue.rs ...
test result: ok. 16 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.68s
Doc-tests calternal_db
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

cargo clippy -p calternal-server --all-targets -- -D warnings
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 2m 22s

cargo test -p calternal-server
test result: ok. 93 passed; 0 failed; 3 ignored; 0 measured; 0 filtered out; finished in 18.45s

python3 -B bench/app-password-dav.py ... --adversarial
WebDAV scripted probes passed

Known gap: the requested perf VM release-binary run remains outstanding because /root/perf.lock was busy. The local comparison used debug binaries on a host with load averages around 29–33.

Issue #512 complete. Branch: `job/apw-cache-512` Base: `cc25c441b7a974185622a1dee853cf38686d2b67` HEAD: `0b97202885034f4b199200eb7e698ad690f3e645` Built one App Password verification cache in `calternal-auth`, shared by the Basic DAV and bearer App Password paths used by DAV/MCP. It uses a per-process keyed BLAKE3 digest, positive entries with a 30-second maximum TTL, immediate generation invalidation on credential/User mutations, and per-credential single-flight verification. Argon2 and App Password/User lookups stay off the single-writer pool. The SQLite read pool default is 64. The benchmark and raw data are in `bench/app-password-dav.py` and `docs/perf/runs/2026-10-01-issue-512-app-password-dav.md`. The perf VM lock was busy, so this was a local Linux debug-binary comparison under high host load. With 50 clients × 5 PROPFIND requests, warm p95 fell from 6,241 ms to 315 ms; cold-start p95 fell from 5,995 ms to 856 ms. The before run returned 429 for 155/250 warm requests and 125/250 cold requests. The after run returned 207 for all requests. `tests/adversarial/webdav.py` passed, and the next DAV request after revocation returned 401. Decisions not stated in DESIGN: use a 30-second cache TTL and a 4,096-entry cap to bound staleness and memory; use 64 read connections for 50 concurrent DAV clients plus background reads; use one shared overflow lock if more than 4,096 unique verifications are in flight. The writer pool remains single-connection. Files changed: `crates/calternal-auth/Cargo.toml`, `crates/calternal-auth/src/store.rs`, `crates/calternal-db/src/db.rs`, `crates/calternal-server/src/wire.rs`, `bench/app-password-dav.py`, `docs/perf/README.md`, and the three `docs/perf/runs/2026-10-01-issue-512-app-password-dav*` result files. Gate output (verbatim): ```text cargo fmt --all --check (exit 0; no stdout) cargo clippy -p calternal-auth --all-targets -- -D warnings Checking calternal-auth v0.1.0 (/home/kayg/Developer/calternal-wt/apw-cache-512/crates/calternal-auth) Finished `dev` profile [unoptimized + debuginfo] target(s) in 1m 08s cargo test -p calternal-auth test result: ok. 67 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 57.25s Doc-tests calternal_auth running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s cargo clippy -p calternal-db --all-targets -- -D warnings Checking calternal-db v0.1.0 (/home/kayg/Developer/calternal-wt/apw-cache-512/crates/calternal-db) Finished `dev` profile [unoptimized + debuginfo] target(s) in 32.81s cargo test -p calternal-db test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.70s Running tests/queue.rs ... test result: ok. 16 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.68s Doc-tests calternal_db running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s cargo clippy -p calternal-server --all-targets -- -D warnings Finished `dev` profile [unoptimized + debuginfo] target(s) in 2m 22s cargo test -p calternal-server test result: ok. 93 passed; 0 failed; 3 ignored; 0 measured; 0 filtered out; finished in 18.45s python3 -B bench/app-password-dav.py ... --adversarial WebDAV scripted probes passed ``` Known gap: the requested perf VM release-binary run remains outstanding because `/root/perf.lock` was busy. The local comparison used debug binaries on a host with load averages around 29–33.
Author
Owner

Independent defensive review started on job/apw-cache-review, base 0b97202885034f4b199200eb7e698ad690f3e645 (job/apw-cache-512). Scope: cache authority ordering, digest construction, resource bounds, DAV isolation and WAL visibility. No push or deploy.

Independent defensive review started on `job/apw-cache-review`, base `0b97202885034f4b199200eb7e698ad690f3e645` (job/apw-cache-512). Scope: cache authority ordering, digest construction, resource bounds, DAV isolation and WAL visibility. No push or deploy.
Author
Owner

App Password cache review — #512

Base: 0b97202885034f4b199200eb7e698ad690f3e645.

Local defects

  1. A cold verification returns old authority when the mutation generation changes. insert_if_current rejects cache insertion but verify_app_password returns Some(authority) without using that result. Fix: reject the current request as well. Add a two-worker ordering test, including zero TTL.
  2. The missing credential row path skips Argon2 because hash.is_some() && verify_password(...) short-circuits. A dummy hash is parsed but not verified. Fix: always do the password check before checking row presence.
  3. The Argon2 semaphore permit belongs to the request future. If that future is cancelled after spawn_blocking, the permit is dropped while the blocking task still runs. Fix: move an owned permit into the blocking task.

Properties that need a design decision

  • The cache uses keyed BLAKE3 with a random process secret, not HMAC. Its digest covers the full credential, including its public credential ID. It does not include the User ID or a scope version. Global mutation generation and username checks supply isolation instead. This does not meet property 3 literally. No key logging was found in the reviewed paths.
  • Digest lookup uses HashMap<[u8; 32], ...> with ordinary array equality, not a constant-time comparison. A keyed digest makes chosen-digest control difficult, but this does not prove the requested constant-time property.
  • Live flight-map entries are capped at 4096; excess distinct credentials share one overflow lock. Waiters have no lock acquisition deadline. The semaphore timeout begins only after lock acquisition. The map is bounded, but pending request futures are not bounded by this cache. Server middleware in wire.rs does not add a request concurrency cap or timeout.
  • Separate SqliteAuthStore::from_pools constructions do not share cache generations. The live server constructs one store and clones it. Independent stores or processes would need a shared invalidation design.
  • Admin demotion clears cached User role but does not revoke App Passwords. Scope changes likewise replace authority rather than revoke the secret. Local accounts use passkeys, so there is no account password-change operation. No App Password secret/scope edit API was found. Treating these events as unconditional credential revocation would change current behaviour.

Limits

Tests and gate results will be added after execution. Timing samples can detect large differences; they cannot prove constant-time execution. Work that changes the cache key design or the HTTP admission design is outside this local fix review.

# App Password cache review — #512 Base: `0b97202885034f4b199200eb7e698ad690f3e645`. ## Local defects 1. A cold verification returns old authority when the mutation generation changes. `insert_if_current` rejects cache insertion but `verify_app_password` returns `Some(authority)` without using that result. Fix: reject the current request as well. Add a two-worker ordering test, including zero TTL. 2. The missing credential row path skips Argon2 because `hash.is_some() && verify_password(...)` short-circuits. A dummy hash is parsed but not verified. Fix: always do the password check before checking row presence. 3. The Argon2 semaphore permit belongs to the request future. If that future is cancelled after `spawn_blocking`, the permit is dropped while the blocking task still runs. Fix: move an owned permit into the blocking task. ## Properties that need a design decision - The cache uses keyed BLAKE3 with a random process secret, not HMAC. Its digest covers the full credential, including its public credential ID. It does not include the User ID or a scope version. Global mutation generation and username checks supply isolation instead. This does not meet property 3 literally. No key logging was found in the reviewed paths. - Digest lookup uses `HashMap<[u8; 32], ...>` with ordinary array equality, not a constant-time comparison. A keyed digest makes chosen-digest control difficult, but this does not prove the requested constant-time property. - Live flight-map entries are capped at 4096; excess distinct credentials share one overflow lock. Waiters have no lock acquisition deadline. The semaphore timeout begins only after lock acquisition. The map is bounded, but pending request futures are not bounded by this cache. Server middleware in `wire.rs` does not add a request concurrency cap or timeout. - Separate `SqliteAuthStore::from_pools` constructions do not share cache generations. The live server constructs one store and clones it. Independent stores or processes would need a shared invalidation design. - Admin demotion clears cached User role but does not revoke App Passwords. Scope changes likewise replace authority rather than revoke the secret. Local accounts use passkeys, so there is no account password-change operation. No App Password secret/scope edit API was found. Treating these events as unconditional credential revocation would change current behaviour. ## Limits Tests and gate results will be added after execution. Timing samples can detect large differences; they cannot prove constant-time execution. Work that changes the cache key design or the HTTP admission design is outside this local fix review.
Author
Owner

Independent review progress: 9dc3e5601 rejects stale in-flight authority, with a real verifier queued behind a held blocking worker; c100f7f78 always runs the dummy hash. Both passed calternal-auth clippy and full tests. Timing diagnostic passed on this shared local debug build: invalid credential p50/p95 ms, known User 664.274/929.980, absent User 564.757/957.436, absent credential row 557.344/1923.225 (12 samples per class). These are coarse work checks, not a constant-time proof.

Additional local finding: a credential can expire during Argon2. Zero cache TTL does not currently reject that completion; lookup also needs the absolute deadline after a clock change. I will fix that locally with a regression test.

Admission finding clarification: the accept loop bounds TCP connections under DESIGN §21. The cache flight map is bounded, but request waiters on its overflow lock have no acquisition deadline. The connection limits do not prove a request-level bound.

Independent review progress: `9dc3e5601` rejects stale in-flight authority, with a real verifier queued behind a held blocking worker; `c100f7f78` always runs the dummy hash. Both passed calternal-auth clippy and full tests. Timing diagnostic passed on this shared local debug build: invalid credential p50/p95 ms, known User 664.274/929.980, absent User 564.757/957.436, absent credential row 557.344/1923.225 (12 samples per class). These are coarse work checks, not a constant-time proof. Additional local finding: a credential can expire during Argon2. Zero cache TTL does not currently reject that completion; lookup also needs the absolute deadline after a clock change. I will fix that locally with a regression test. Admission finding clarification: the accept loop bounds TCP connections under DESIGN §21. The cache flight map is bounded, but request waiters on its overflow lock have no acquisition deadline. The connection limits do not prove a request-level bound.
Author
Owner

Additional local finding: both digest maps convert blake3::Hash to [u8; 32]. The locked BLAKE3 1.8.7 source states that byte arrays lack its constant-time equality guarantee, and its Hash::PartialEq calls constant_time_eq_32. I am retaining blake3::Hash in both cache and flight maps, with compile-time key-type checks and functional mismatches at all 32 byte positions. This changes equality, not the keyed digest algorithm or key inputs.

WAL regression passed: four existing reader connections each observed all 1,000 writer commits immediately. Auth review tests passed (75 passed, one timing diagnostic ignored and separately executed). The perf VM lock was busy at the nonblocking check; performance will be labelled local.

Additional local finding: both digest maps convert `blake3::Hash` to `[u8; 32]`. The locked BLAKE3 1.8.7 source states that byte arrays lack its constant-time equality guarantee, and its `Hash::PartialEq` calls `constant_time_eq_32`. I am retaining `blake3::Hash` in both cache and flight maps, with compile-time key-type checks and functional mismatches at all 32 byte positions. This changes equality, not the keyed digest algorithm or key inputs. WAL regression passed: four existing reader connections each observed all 1,000 writer commits immediately. Auth review tests passed (75 passed, one timing diagnostic ignored and separately executed). The perf VM lock was busy at the nonblocking check; performance will be labelled local.
Author
Owner

App Password cache review — #512

Head: 950ade5bcc9e71729d38d9c53e72bfbe6fba113b. Branch: job/apw-cache-review.

Base: 0b97202885034f4b199200eb7e698ad690f3e645.

Fixed local defects

  1. A cold verification returns old authority when the mutation generation changes. insert_if_current rejects cache insertion but verify_app_password returns Some(authority) without using that result. Fix: reject the current request as well. Add a two-worker ordering test, including zero TTL.

  2. The missing credential row path skips Argon2 because hash.is_some() && verify_password(...) short-circuits. A dummy hash is parsed but not verified. Fix: always do the password check before checking row presence.

  3. The Argon2 semaphore permit belongs to the request future. If that future is cancelled after spawn_blocking, the permit is dropped while the blocking task still runs. Fix: move an owned permit into the blocking task.

  4. A row can expire between the SELECT and the end of Argon2. Zero TTL stops insertion but still accepts expired authority. Cache lookup also relies only on monotonic TTL after a wall-clock change. Fix: check the absolute deadline on completion and lookup.

  5. The digest maps discard blake3::Hash and use [u8; 32]. The locked BLAKE3 1.8.7 source explicitly warns that this conversion loses constant-time equality. Fix: retain blake3::Hash in both maps. Add compile-time map-key checks and mismatches at each byte.

Properties that need a design decision

  • The cache uses keyed BLAKE3 with a random process secret, not HMAC. Its digest covers the full credential, including its public credential ID. It does not include the User ID or a scope version. Global mutation generation and username checks supply isolation instead. This does not meet property 3 literally. No key logging was found in the reviewed paths.
  • Constant-time digest equality does not make the whole hash-table lookup or request path constant-time. Cold and warm valid requests have different costs by design. Coarse invalid response checks do not prove the absence of every timing side channel.
  • Live flight-map entries are capped at 4096; excess distinct credentials share one overflow lock. Waiters have no lock acquisition deadline. The semaphore timeout begins only after lock acquisition. The map is bounded, but pending request futures are not bounded by this cache. Server middleware in wire.rs does not add a request concurrency cap or timeout. DESIGN §21 and the TCP accept loop do bound connections (256 per direct peer; 4096 total through trusted proxies). A connection bound is not a per-request bound.
  • Global invalidation also rejects in-flight verification for unrelated Users. Per-User or per-credential generations would reduce these false denials. This review retains fail-closed behaviour.
  • Separate SqliteAuthStore::from_pools constructions do not share cache generations. The live server constructs one store and clones it. Independent stores or processes would need a shared invalidation design.
  • Admin demotion clears cached User role but does not revoke App Passwords. Scope changes likewise replace authority rather than revoke the secret. Local accounts use passkeys, so there is no account password-change operation. No App Password secret/scope edit API was found. Treating these events as unconditional credential revocation would change current behaviour.

Evidence by property

Property Evidence Limit
1. Immediate change Controlled blocking-worker tests for revoke, secret replacement, scope replacement, disable, deletion and demotion; queued single-flight follower; sequential User-state matrix Secret and scope edit APIs do not exist. Fixture SQL writes use the documented invalidation hook. Demotion and scope change refresh authority but retain the credential. Requests that passed authentication before revocation are outside the completion check.
2. Timing Dummy Argon2 work always runs; blake3::Hash retains documented constant-time digest equality; compile-time map-key tests and 32 byte-position mismatches; 12 paired invalid samples per class Hash-table lookup and complete responses are not constant-time. Full-request timing checks are coarse.
3. Keyed digest Bounds/key test checks the digest differs from a plain hash and from the same credential under another process key; source has no key logging The MAC is keyed BLAKE3, not HMAC. User and scope version are not explicit key inputs.
4. Bounds 4096-entry positive and flight-map caps; 30-second TTL; expired lookup cleanup; cancellation retains hashing permit; failed attempts do not create positives or block a following valid check Flight-lock waiters have no deadline or request-count limit in the cache.
5. Scope and User Two-User cold/warm cache matrix; 4-by-4 surface scope matrix; two-User live DAV matrix The two-User DAV router matrix passed in both rounds. API/MCP surface coverage is a route-guard unit test.
6. WAL Four existing reader connections each see 1000 writer commits immediately All 1000 API-revoke/DAV checks passed. The test reuses a fixture row and exercises the full router without TCP. The TCP profile is a separate check.

Final auth: 76 passed, one timing diagnostic ignored and separately executed. DB: 12 unit tests and 16 integration tests passed; one pre-existing test is ignored. Server: 94 passed, four ignored, including this live audit. The live audit passed separately: 1000 immediate revoke/DAV checks, two-User DAV isolation, and invalid response timing. It completed in 1324.56 seconds on the shared host. Timing samples can detect large differences; they cannot prove constant-time execution. Work that changes the cache key design or the HTTP admission design is outside this local fix review.

Timing evidence

Each invalid class has 12 samples. The p95 is the maximum sample under the nearest-rank method. These results are local debug measurements on a shared host.

Class Auth-store p50 / p95 ms Full DAV router p50 / p95 ms
Known User, wrong secret 664.274 / 929.980 855.968 / 1619.543
Absent User, wrong secret 564.757 / 957.436 813.961 / 1539.347
Absent credential row 557.344 / 1923.225 938.097 / 1135.592

The code selects credentials by credential ID, not username. For a wrong secret, it does not query the User row. An invalid username does not select another User. Successful warm and cold requests have different work by design; the performance profile measures both.

Decisions

  • Reject an overlapping verification. Do not retry it automatically. This keeps the mutation boundary fail-closed.
  • Keep keyed BLAKE3 and the global mutation generation. Changing the key inputs requires a separate design decision.
  • Keep current demotion and scope-change behaviour. Re-read current authority; do not revoke the unchanged secret. DESIGN §21 explicitly revokes sessions on role changes.
  • Keep the expensive 1000-iteration live test ignored in routine gates. Run it once in its own process for this review.
  • Use the existing #512 DAV benchmark for the unchanged successful hot path. The perf VM lock was busy at one nonblocking check, so this review uses local debug measurements.

Performance

The perf VM lock was busy. This run uses the local debug server at 950ade5bcc9e71729d38d9c53e72bfbe6fba113b, 50 clients, and five requests per client in each phase. All 500 responses were HTTP 207. After API revocation, the next TCP DAV request was HTTP 401.

docs/perf/baseline.json has no App Password DAV metric. The reference below is the existing local after-cache run in docs/perf/runs/2026-10-01-issue-512-app-password-dav.md. Host load and sample duration differ; this is not a controlled regression measurement.

Phase Existing #512 p50 / p95 ms Review p50 / p95 ms Existing mean CPU / RSS MiB Review mean CPU / RSS MiB
warm_burst 218 / 315 59.547 / 73.769 59.92% / 161.0 136.57% / 174.9
cold_start_burst 224 / 856 86.769 / 599.516 65.76% / 124.8 112.31% / 128.3

Review resource samples: three warm samples and five cold samples. Load averages were 15.53/17.49/22.33. Peak RSS was 177.9 MiB warm and 139.9 MiB cold. CPU percentage alone does not compare CPU cost per request when throughput differs. No perf-VM regression result is available.

Files and commits

  • crates/calternal-auth/src/store.rs: five local fixes, cache property tests, timing diagnostic and controlled worker-ordering tests.
  • crates/calternal-db/src/db.rs: 1000 writer commits observed on four existing WAL readers.
  • crates/calternal-server/src/wire.rs: 4-by-4 surface matrix; two-User DAV matrix; response timings; 1000 API-revoke/DAV checks.
  • artifacts/apw-cache-review/findings.md: full review evidence and known gaps; not committed because artifacts are ignored.

950ade5bc Add DAV scope and revocation audit with repeated API writes
f8df61943 Preserve constant-time BLAKE3 digest equality in cache maps
b68869d0d Verify WAL readers observe a thousand committed writer updates
c2d71efe9 Test cache bounds, User isolation and concurrent authority changes
0c9fa11e0 Recheck App Password expiry after verification and on cache hits
7eb7e3c14 Hold App Password hashing permits until blocking work finishes
c100f7f78 Always verify dummy App Password hashes to equalise missing-row work
9dc3e5601 Reject App Password results that overlap authority invalidation

Gates — verbatim output excerpts

cargo fmt --check: exit 0, no output.

cargo clippy -p calternal-auth --all-targets -- -D warnings

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1m 04s

cargo test -p calternal-auth

test result: ok. 76 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 34.73s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

cargo clippy -p calternal-db --all-targets -- -D warnings

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1m 03s

cargo test -p calternal-db

test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.78s
test result: ok. 16 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.78s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

cargo clippy -p calternal-server --all-targets -- -D warnings

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 11m 43s

cargo test -p calternal-server

test result: ok. 94 passed; 0 failed; 4 ignored; 0 measured; 0 filtered out; finished in 13.25s

cargo test -p calternal-server wire::tests::review_dav_cache_isolation_and_immediate_api_revocation -- --exact --ignored --nocapture

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 97 filtered out; finished in 1324.56s

cargo test -p calternal-auth review_invalid_credential_timing_distributions -- --ignored --nocapture

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 69 filtered out; finished in 24.54s

Full gate output is in the named log files under artifacts/apw-cache-review/. The unchanged frontend production build passed as a server build prerequisite. No web source changed.

Cleanup and disposition

No push or deploy. The required git fetch origin and git merge origin/dev completed with Already up to date. No issue was closed. Doc comments in all three files were read again. The working tree is clean. Web build output and generated Python bytecode were removed.

cargo clean output:

     Removed 16365 files, 8.4GiB total

The five local defects are fixed and tested. The literal HMAC/User/scope-version requirement, flight-waiter admission bounds, and unconditional credential revocation on demotion or scope change remain unresolved. This report does not approve those properties by inference.

# App Password cache review — #512 Head: `950ade5bcc9e71729d38d9c53e72bfbe6fba113b`. Branch: `job/apw-cache-review`. Base: `0b97202885034f4b199200eb7e698ad690f3e645`. ## Fixed local defects 1. A cold verification returns old authority when the mutation generation changes. `insert_if_current` rejects cache insertion but `verify_app_password` returns `Some(authority)` without using that result. Fix: reject the current request as well. Add a two-worker ordering test, including zero TTL. 2. The missing credential row path skips Argon2 because `hash.is_some() && verify_password(...)` short-circuits. A dummy hash is parsed but not verified. Fix: always do the password check before checking row presence. 3. The Argon2 semaphore permit belongs to the request future. If that future is cancelled after `spawn_blocking`, the permit is dropped while the blocking task still runs. Fix: move an owned permit into the blocking task. 4. A row can expire between the SELECT and the end of Argon2. Zero TTL stops insertion but still accepts expired authority. Cache lookup also relies only on monotonic TTL after a wall-clock change. Fix: check the absolute deadline on completion and lookup. 5. The digest maps discard `blake3::Hash` and use `[u8; 32]`. The locked BLAKE3 1.8.7 source explicitly warns that this conversion loses constant-time equality. Fix: retain `blake3::Hash` in both maps. Add compile-time map-key checks and mismatches at each byte. ## Properties that need a design decision - The cache uses keyed BLAKE3 with a random process secret, not HMAC. Its digest covers the full credential, including its public credential ID. It does not include the User ID or a scope version. Global mutation generation and username checks supply isolation instead. This does not meet property 3 literally. No key logging was found in the reviewed paths. - Constant-time digest equality does not make the whole hash-table lookup or request path constant-time. Cold and warm valid requests have different costs by design. Coarse invalid response checks do not prove the absence of every timing side channel. - Live flight-map entries are capped at 4096; excess distinct credentials share one overflow lock. Waiters have no lock acquisition deadline. The semaphore timeout begins only after lock acquisition. The map is bounded, but pending request futures are not bounded by this cache. Server middleware in `wire.rs` does not add a request concurrency cap or timeout. DESIGN §21 and the TCP accept loop do bound connections (256 per direct peer; 4096 total through trusted proxies). A connection bound is not a per-request bound. - Global invalidation also rejects in-flight verification for unrelated Users. Per-User or per-credential generations would reduce these false denials. This review retains fail-closed behaviour. - Separate `SqliteAuthStore::from_pools` constructions do not share cache generations. The live server constructs one store and clones it. Independent stores or processes would need a shared invalidation design. - Admin demotion clears cached User role but does not revoke App Passwords. Scope changes likewise replace authority rather than revoke the secret. Local accounts use passkeys, so there is no account password-change operation. No App Password secret/scope edit API was found. Treating these events as unconditional credential revocation would change current behaviour. ## Evidence by property | Property | Evidence | Limit | | --- | --- | --- | | 1. Immediate change | Controlled blocking-worker tests for revoke, secret replacement, scope replacement, disable, deletion and demotion; queued single-flight follower; sequential User-state matrix | Secret and scope edit APIs do not exist. Fixture SQL writes use the documented invalidation hook. Demotion and scope change refresh authority but retain the credential. Requests that passed authentication before revocation are outside the completion check. | | 2. Timing | Dummy Argon2 work always runs; `blake3::Hash` retains documented constant-time digest equality; compile-time map-key tests and 32 byte-position mismatches; 12 paired invalid samples per class | Hash-table lookup and complete responses are not constant-time. Full-request timing checks are coarse. | | 3. Keyed digest | Bounds/key test checks the digest differs from a plain hash and from the same credential under another process key; source has no key logging | The MAC is keyed BLAKE3, not HMAC. User and scope version are not explicit key inputs. | | 4. Bounds | 4096-entry positive and flight-map caps; 30-second TTL; expired lookup cleanup; cancellation retains hashing permit; failed attempts do not create positives or block a following valid check | Flight-lock waiters have no deadline or request-count limit in the cache. | | 5. Scope and User | Two-User cold/warm cache matrix; 4-by-4 surface scope matrix; two-User live DAV matrix | The two-User DAV router matrix passed in both rounds. API/MCP surface coverage is a route-guard unit test. | | 6. WAL | Four existing reader connections each see 1000 writer commits immediately | All 1000 API-revoke/DAV checks passed. The test reuses a fixture row and exercises the full router without TCP. The TCP profile is a separate check. | Final auth: 76 passed, one timing diagnostic ignored and separately executed. DB: 12 unit tests and 16 integration tests passed; one pre-existing test is ignored. Server: 94 passed, four ignored, including this live audit. The live audit passed separately: 1000 immediate revoke/DAV checks, two-User DAV isolation, and invalid response timing. It completed in 1324.56 seconds on the shared host. Timing samples can detect large differences; they cannot prove constant-time execution. Work that changes the cache key design or the HTTP admission design is outside this local fix review. ## Timing evidence Each invalid class has 12 samples. The p95 is the maximum sample under the nearest-rank method. These results are local debug measurements on a shared host. | Class | Auth-store p50 / p95 ms | Full DAV router p50 / p95 ms | | --- | ---: | ---: | | Known User, wrong secret | 664.274 / 929.980 | 855.968 / 1619.543 | | Absent User, wrong secret | 564.757 / 957.436 | 813.961 / 1539.347 | | Absent credential row | 557.344 / 1923.225 | 938.097 / 1135.592 | The code selects credentials by credential ID, not username. For a wrong secret, it does not query the User row. An invalid username does not select another User. Successful warm and cold requests have different work by design; the performance profile measures both. ## Decisions - Reject an overlapping verification. Do not retry it automatically. This keeps the mutation boundary fail-closed. - Keep keyed BLAKE3 and the global mutation generation. Changing the key inputs requires a separate design decision. - Keep current demotion and scope-change behaviour. Re-read current authority; do not revoke the unchanged secret. DESIGN §21 explicitly revokes sessions on role changes. - Keep the expensive 1000-iteration live test ignored in routine gates. Run it once in its own process for this review. - Use the existing #512 DAV benchmark for the unchanged successful hot path. The perf VM lock was busy at one nonblocking check, so this review uses local debug measurements. ## Performance The perf VM lock was busy. This run uses the local debug server at `950ade5bcc9e71729d38d9c53e72bfbe6fba113b`, 50 clients, and five requests per client in each phase. All 500 responses were HTTP 207. After API revocation, the next TCP DAV request was HTTP 401. `docs/perf/baseline.json` has no App Password DAV metric. The reference below is the existing local after-cache run in `docs/perf/runs/2026-10-01-issue-512-app-password-dav.md`. Host load and sample duration differ; this is not a controlled regression measurement. | Phase | Existing #512 p50 / p95 ms | Review p50 / p95 ms | Existing mean CPU / RSS MiB | Review mean CPU / RSS MiB | | --- | ---: | ---: | ---: | ---: | | warm_burst | 218 / 315 | 59.547 / 73.769 | 59.92% / 161.0 | 136.57% / 174.9 | | cold_start_burst | 224 / 856 | 86.769 / 599.516 | 65.76% / 124.8 | 112.31% / 128.3 | Review resource samples: three warm samples and five cold samples. Load averages were 15.53/17.49/22.33. Peak RSS was 177.9 MiB warm and 139.9 MiB cold. CPU percentage alone does not compare CPU cost per request when throughput differs. No perf-VM regression result is available. ## Files and commits - `crates/calternal-auth/src/store.rs`: five local fixes, cache property tests, timing diagnostic and controlled worker-ordering tests. - `crates/calternal-db/src/db.rs`: 1000 writer commits observed on four existing WAL readers. - `crates/calternal-server/src/wire.rs`: 4-by-4 surface matrix; two-User DAV matrix; response timings; 1000 API-revoke/DAV checks. - `artifacts/apw-cache-review/findings.md`: full review evidence and known gaps; not committed because artifacts are ignored. 950ade5bc Add DAV scope and revocation audit with repeated API writes f8df61943 Preserve constant-time BLAKE3 digest equality in cache maps b68869d0d Verify WAL readers observe a thousand committed writer updates c2d71efe9 Test cache bounds, User isolation and concurrent authority changes 0c9fa11e0 Recheck App Password expiry after verification and on cache hits 7eb7e3c14 Hold App Password hashing permits until blocking work finishes c100f7f78 Always verify dummy App Password hashes to equalise missing-row work 9dc3e5601 Reject App Password results that overlap authority invalidation ## Gates — verbatim output excerpts `cargo fmt --check`: exit 0, no output. `cargo clippy -p calternal-auth --all-targets -- -D warnings` ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 1m 04s ``` `cargo test -p calternal-auth` ```text test result: ok. 76 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 34.73s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` `cargo clippy -p calternal-db --all-targets -- -D warnings` ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 1m 03s ``` `cargo test -p calternal-db` ```text test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.78s test result: ok. 16 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.78s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` `cargo clippy -p calternal-server --all-targets -- -D warnings` ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 11m 43s ``` `cargo test -p calternal-server` ```text test result: ok. 94 passed; 0 failed; 4 ignored; 0 measured; 0 filtered out; finished in 13.25s ``` `cargo test -p calternal-server wire::tests::review_dav_cache_isolation_and_immediate_api_revocation -- --exact --ignored --nocapture` ```text test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 97 filtered out; finished in 1324.56s ``` `cargo test -p calternal-auth review_invalid_credential_timing_distributions -- --ignored --nocapture` ```text test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 69 filtered out; finished in 24.54s ``` Full gate output is in the named log files under `artifacts/apw-cache-review/`. The unchanged frontend production build passed as a server build prerequisite. No web source changed. ## Cleanup and disposition No push or deploy. The required `git fetch origin` and `git merge origin/dev` completed with `Already up to date.` No issue was closed. Doc comments in all three files were read again. The working tree is clean. Web build output and generated Python bytecode were removed. `cargo clean` output: ```text Removed 16365 files, 8.4GiB total ``` The five local defects are fixed and tested. The literal HMAC/User/scope-version requirement, flight-waiter admission bounds, and unconditional credential revocation on demotion or scope change remain unresolved. This report does not approve those properties by inference.
Author
Owner

Round 2 started on job/apw-cache-review, base/head 950ade5bcc. I will close the explicit HMAC identity/scope key, bounded verification admission, and unconditional authority invalidation properties, then run per-crate gates and the live DAV/revocation profiles. No push or deployment.

Round 2 started on job/apw-cache-review, base/head 950ade5bcc9e71729d38d9c53e72bfbe6fba113b. I will close the explicit HMAC identity/scope key, bounded verification admission, and unconditional authority invalidation properties, then run per-crate gates and the live DAV/revocation profiles. No push or deployment.
Author
Owner

Finding: the same secret inserted for two Users collapsed to one positive entry (regression test: left 1, right 2). HMAC-SHA256 now binds fixed-size User ID, credential ID, scope version (mutation generation plus SHA256 of all scope restrictions), and credential secret. A separate bounded, domain-separated HMAC locator finds the full key without an Index read on warm hits. Production loads auth.app-password-cache.v1 once through the existing instance_secrets store and persists it on first startup; test-only standalone constructors use ephemeral restart-random keys. Targeted identity regression now passes. Existing overflow flight lock still requires the next fix.

Finding: the same secret inserted for two Users collapsed to one positive entry (regression test: left 1, right 2). HMAC-SHA256 now binds fixed-size User ID, credential ID, scope version (mutation generation plus SHA256 of all scope restrictions), and credential secret. A separate bounded, domain-separated HMAC locator finds the full key without an Index read on warm hits. Production loads auth.app-password-cache.v1 once through the existing instance_secrets store and persists it on first startup; test-only standalone constructors use ephemeral restart-random keys. Targeted identity regression now passes. Existing overflow flight lock still requires the next fix.
Author
Owner

Committed HMAC key slice 77ad26395 and bounded admission slice bf8f61217. Auth clippy passed; auth tests: test result: ok. 80 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 78.14s. The combined 10,000 distinct bogus credentials + 1,000 same-credential waiters test passed with <=64 flights, <=64 followers per flight, <=256 admitted cold requests and <=4 hashes. Warm valid authentication passed during and after saturation. 429 now carries Retry-After: 1. The perf VM lock was busy at the nonblocking check; measurements will be local. Demotion currently clears the cache but reauthenticates the same credential with the new role; I will enforce the job’s next-request-fails requirement by revoking credentials on demotion and scope change.

Committed HMAC key slice 77ad26395 and bounded admission slice bf8f61217. Auth clippy passed; auth tests: test result: ok. 80 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 78.14s. The combined 10,000 distinct bogus credentials + 1,000 same-credential waiters test passed with <=64 flights, <=64 followers per flight, <=256 admitted cold requests and <=4 hashes. Warm valid authentication passed during and after saturation. 429 now carries Retry-After: 1. The perf VM lock was busy at the nonblocking check; measurements will be local. Demotion currently clears the cache but reauthenticates the same credential with the new role; I will enforce the job’s next-request-fails requirement by revoking credentials on demotion and scope change.
Author
Owner

Mutation finding: clearing the cache on demotion still let the same secret authenticate as the new standard User. The new regression failed before the fix and passes after it. Demotion, disable and scope changes now retire the old credential transactionally. A change-window guard blocks cache hits/positive writes before the first write; an owned commit task holds it through commit even if the caller is cancelled. Multi-worker test passed 1,000 demotions, 1,000 disables, 1,000 scope changes and 1,000 physical deletions, each clearing unrelated positive entries and rejecting a late writer. Auth clippy passed; test result: ok. 83 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 102.46s. Two existing expectations were changed because this round explicitly requires next-request failure: demotion no longer reauthenticates the old secret as Member; scope change no longer returns new scopes through the old secret. A new credential is required. No new scope-edit HTTP endpoint was added.

Mutation finding: clearing the cache on demotion still let the same secret authenticate as the new standard User. The new regression failed before the fix and passes after it. Demotion, disable and scope changes now retire the old credential transactionally. A change-window guard blocks cache hits/positive writes before the first write; an owned commit task holds it through commit even if the caller is cancelled. Multi-worker test passed 1,000 demotions, 1,000 disables, 1,000 scope changes and 1,000 physical deletions, each clearing unrelated positive entries and rejecting a late writer. Auth clippy passed; test result: ok. 83 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 102.46s. Two existing expectations were changed because this round explicitly requires next-request failure: demotion no longer reauthenticates the old secret as Member; scope change no longer returns new scopes through the old secret. A new credential is required. No new scope-edit HTTP endpoint was added.
Author
Owner

Read-only review found two further edge cases. Regression evidence: suffix variants for one credential ID created 64 flights instead of 1; a correct credential during an unrelated change did not return retryable Unavailable. Both regressions failed on the previous slice. The fix now groups flights by canonical credential UUID under a separate HMAC domain. Active mutation contention returns 503 with Retry-After: 1; a late generation rejection rechecks only the exceptional credential/User state so real revocation/hash change/disable/delete remains 401, while unrelated change cannot increment the failure limiter. A deterministic queued-verifier test covers the late unrelated-change path. Follow-up read-only review found no remaining critical/important issue. New green tests and final gates are still running. origin/dev was fetched and merged once: Already up to date.

Read-only review found two further edge cases. Regression evidence: suffix variants for one credential ID created 64 flights instead of 1; a correct credential during an unrelated change did not return retryable Unavailable. Both regressions failed on the previous slice. The fix now groups flights by canonical credential UUID under a separate HMAC domain. Active mutation contention returns 503 with Retry-After: 1; a late generation rejection rechecks only the exceptional credential/User state so real revocation/hash change/disable/delete remains 401, while unrelated change cannot increment the failure limiter. A deterministic queued-verifier test covers the late unrelated-change path. Follow-up read-only review found no remaining critical/important issue. New green tests and final gates are still running. origin/dev was fetched and merged once: Already up to date.
Author
Owner

Round 2 complete on job/apw-cache-review. Head: e076d611258627f151fa6be6149e37ae2f6e4194. Base: 950ade5bcc9e71729d38d9c53e72bfbe6fba113b.

Built:

  • HMAC-SHA256 positive keys bind server key, User ID, credential ID, scope version and secret. Tests cover different Users, scopes and a version-only cache miss.
  • Canonical credential flights cap 64 distinct flights, 64 waiters plus one leader per credential, and 256 admitted cold requests. Four password hash jobs can run. Excess admission returns HTTP 429; temporary authority changes return HTTP 503. Both include Retry-After: 1. The 10,000-distinct plus 1,000-same-credential storm passed and a warm valid User still authenticated.
  • Authority changes unconditionally clear positive entries and fence late writes with a generation counter. The owned commit task holds the change guard even after request cancellation. Demotion, disable, deletion and scope changes retire the old App Password. Four multi-worker tests passed 1,000 transitions each. Secret suffix and UUID case variants share flight limits. Unrelated authority changes return a retry rather than counting against bad-password limits.
  • The server loads its persisted 32-byte HMAC key once through the existing Index secret store. Server deletion commits use the same authority fence.
  • The live DAV profile now runs repeated warm/revoke/next-request checks through HTTP.

Files: Cargo.lock; crates/calternal-auth/Cargo.toml; crates/calternal-auth/src/store.rs, error.rs, lib.rs; crates/calternal-server/src/wire.rs; bench/app-password-dav.py; docs/perf/runs/2026-10-01-issue-512-app-password-dav.md; docs/perf/runs/2026-10-01-issue-512-round2-app-password-dav.json. No calternal-db source change was needed.

Live validation: 50 clients, five requests each per phase, 250 HTTP 207 responses in each phase. Warm p50/p95: 123.027/150.034 ms; cold-start p50/p95: 88.960/626.574 ms. Warm mean CPU/RSS/peak RSS: 89.65%/153.9 MiB/155.7 MiB; cold: 108.15%/127.4 MiB/139.4 MiB. Host load: 11.47/12.77/19.26. All 1,000 live cycles returned warm 207, revoke 204, next DAV 401. The one scripted WebDAV adversarial round passed. Round 1 warm p50/p95 was 218/315 ms; cold was 224/856 ms. Neither p95 nor peak RSS crosses its published regression budget. docs/perf/baseline.json has no comparable App Password DAV profile. These are local debug measurements because the perf VM lock was busy.

Decisions:

  • Use the existing persisted key slot auth.app-password-cache.v1. A missing key is generated and persisted; production restarts keep it. Invalid size stops startup. Standalone/test stores use random ephemeral keys.
  • Encode scope version as the mutation counter plus SHA256 of the scope restrictions, including Home prefix and Plugin scope. Fixed-width UUIDs and counter avoid ambiguous concatenation.
  • Retire old credentials after demotion, disable or scope change to meet the required next-request failure. A User issues a new App Password afterward. The scope-change API is a trusted store hook, with no new HTTP endpoint.
  • Retain the existing instance-wide generation counter. An unrelated change can cause a short retryable 503.
  • Use the admission limits listed above, a 30-second positive TTL, 4,096 entries and five-second wait deadlines.

Known gaps: quiet release measurement on the perf VM remains unavailable. No UI changed. Manual timing/throughput diagnostics remain ignored. The server's ignored live-app tests are exercised by its separate-process wrapper, except the older manual DAV review diagnostic; the new live runner supplies the requested 1,000 HTTP revocation checks. Existing demotion and scope expectations changed only because this issue explicitly requires old credentials to fail on the next request; this was disclosed in the earlier finding comment. The read-only reviewer found no remaining concrete critical or important finding after the fixes.

Integration: fetched origin/dev once and merged once; output was Already up to date. No push, deploy or merge into dev/main. Module and changed function comments were read again. cargo clean removed 16,371 files / 8.5 GiB; web build output and Python caches were deleted. Working tree is clean.

Gate output verbatim follows. All commands exited 0. Environment: CARGO_PROFILE_DEV_DEBUG=line-tables-only, CARGO_INCREMENTAL=0, CARGO_BUILD_JOBS=4, RUST_TEST_THREADS=4, TMPDIR=/target/tmp. CARGO_TARGET_DIR was not changed.

cargo fmt --check: no output; exit 0.

cargo clippy -p calternal-auth --all-targets -- -D warnings

    Checking calternal-auth v0.1.0 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-auth)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 42.95s

cargo test -p calternal-auth

   Compiling calternal-auth v0.1.0 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-auth)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 18.28s
     Running unittests src/lib.rs (/mnt/hdd/targets/jobs/apw-cache-review/debug/deps/calternal_auth-8c6bf3e6451def45)

running 87 tests
test api::cli_login::tests::redirect_is_loopback_only ... ok
test api::cli_login::tests::approval_requires_a_browser_cookie_without_bearer_override ... ok
test api::cli_login::tests::signed_out_browser_gets_a_sign_in_page_not_a_json_error ... ok
test api::tests::apple_profile_is_a_valid_plist_with_the_cal_dav_payload ... ok
test api::tests::agent_token_cannot_extract_account_authority ... ok
test api::cli_login::tests::device_code_is_one_time_and_bound_to_verifier_and_web_user ... ok
test api::tests::admin_lists_need_admin_scope_and_revocation_needs_fresh_assertion ... ok
test api::tests::ask_agent_scope_allows_reads_and_denies_write_methods ... ok
test api::tests::auth_options_report_setup_and_signup_without_a_session ... ok
test api::tests::forwarded_chain_uses_first_untrusted_hop_from_right ... ok
test api::tests::authority_routes_require_recent_assertion_on_same_session ... ok
test api::tests::profile_tokens_expire_and_are_consumed_once ... ok
test api::tests::rate_limit_isolated_by_peer_ip ... ok
test api::tests::extractor_keeps_cookie_and_bearer_session_kinds_separate ... ok
test api::tests::session_cookie_outlives_the_browser_session ... ok
test error::tests::bounded_admission_has_retry_after ... ok
test api::tests::passkey_rename_security_summary_and_current_session ... ok
test oidc::tests::groups_authoritative_defaults_to_true_in_provider_config ... ok
test api::tests::sign_out_revokes_only_the_calling_session_without_fresh_assertion ... ok
test oidc::tests::existing_identity_reconciles_groups_by_default ... ok
test oidc::tests::non_authoritative_groups_only_set_initial_role ... ok
test api::tests::browser_callback_finishes_only_in_the_starting_browser ... ok
test passkey::tests::existing_registration_fails_after_initiating_session_revoked ... ok
test passkey::tests::setup_registration_then_simulated_usernameless_login ... ok
test profile_signing::tests::cms_profile_is_attached_der_sha256_and_contains_the_chain ... ok
test profile_signing::tests::expired_signing_certificate_is_rejected ... ok
test profile_signing::tests::status_hides_the_certificate_team_id ... ok
test profile_signing::tests::status_warns_when_the_chain_expires_within_thirty_days ... ok
test profile_signing::tests::unsigned_fallback_preserves_the_profile_bytes ... ok
test recovery::tests::checksum_and_normalization ... ok
test oidc::tests::oidc_reauth_marks_only_initiating_session ... ok
test store::tests::admin_reenrol_link_is_single_use ... ok
test store::tests::agent_token_has_data_scope_and_home_shares_limit ... ok
test store::tests::all_auth_migrations_run_in_order_on_an_empty_database ... ok
test store::tests::app_password_cache_coalesces_concurrent_misses ... ok
test store::tests::app_password_cache_rejects_expired_completion_and_hit ... ok
test store::tests::app_password_cache_rejects_in_flight_authority ... ok
test store::tests::app_password_cache_rejects_late_verification_after_invalidation ... ok
test store::tests::app_password_cancellation_keeps_blocking_work_bounded ... ok
test store::tests::app_password_digest_maps_preserve_constant_time_equality ... ok
test store::tests::account_security_counts_legacy_codes_and_lists_issuers ... ok
test store::tests::app_password_options_validate_protocols_and_home_prefixes ... ok
test store::tests::app_password_revoke_rejects_queued_verification ... ok
test store::tests::app_password_scope_migration_preserves_existing_caldav_rights ... ok
test store::tests::disabling_user_invalidates_app_password_authority ... ok
test store::tests::display_names_refuse_bidi_and_control_characters ... ok
test store::tests::human_session_scopes_follow_role ... ok
test store::tests::app_password_is_one_time_secret_bound_to_user_and_revocable ... ok
test store::tests::invites_list_without_tokens_and_revoke_only_unconsumed ... ok
test store::tests::migration_revokes_unclassified_pre_scope_sessions ... ok
test store::tests::oidc_cannot_claim_first_user_or_owner_role ... ok
test store::tests::oidc_reconcile_downgrades_and_preserves_owner ... ok
test store::tests::pool_acquisition_timeout_is_unavailable_not_internal ... ok
test store::tests::profile_role_and_disable_changes_take_effect ... ok
test store::tests::quota_overrides_follow_invites_and_require_an_admin ... ok
test store::tests::records_an_admin_action_in_security_events ... ok
test oidc::tests::groups_claim_maps_admin_and_guest ... ok
test store::tests::key_rotation_revokes_sessions_and_rejects_competing_recovery ... ok
test store::tests::rename_passkey_is_owner_only_and_validates_the_label ... ok
test store::tests::recovery_code_is_one_time ... ok
test oidc::tests::validates_nonce_audience_expiry_and_refreshes_rotated_key ... ok
test store::tests::recovery_proof_is_checked_before_challenge ... ok
test store::tests::review_change_window_rejects_positive_writes ... ok
test store::tests::review_demotion_revokes_app_password ... ok
test store::tests::review_invalid_credential_timing_distributions ... ignored, timing diagnostic; run once with --ignored --nocapture
test store::tests::review_identity_bound_cache_key ... ok
test store::tests::review_cache_bounds_ttl_and_keyed_digest ... ok
test store::tests::review_round_two_suffixes_share_credential_admission ... ok
test store::tests::review_round_two_late_unrelated_change_is_retryable ... ok
test store::tests::review_round_two_unrelated_change_is_retryable ... ok
test store::tests::review_failed_attempts_and_two_user_cache_matrix ... ok
test store::tests::review_mutations_reject_queued_verification ... ok
test store::tests::review_waiter_admission_is_bounded ... ok
test store::tests::revoke_all_keeps_only_current_and_requires_fresh_for_changes ... ok
test store::tests::role_and_disable_revoke_sessions_and_event_failure_rolls_back ... ok
test store::tests::review_ten_thousand_credentials_and_thousand_waiters ... ok
test store::tests::scope_change_uses_shared_app_password_cache_invalidation ... ok
test store::tests::sessions_are_kind_bound_and_revocable ... ok
test store::tests::sessions_expire_at_idle_and_absolute_deadlines ... ok
test store::tests::setup_is_single_use_under_concurrency ... ok
test store::tests::transfer_target_cannot_be_deleted_until_pending_transfer_finishes ... ok
test store::tests::unlink_oidc_keeps_a_credential_and_rolls_back_on_audit_failure ... ok
test store::tests::user_deletion_revokes_sessions_and_resumes_with_the_original_expiry ... ok
test store::tests::username_is_case_insensitive_and_invite_single_use ... ok
test store::tests::review_user_and_secret_mutation_matrix ... ok
test store::tests::session_authority_answers_while_the_writer_pool_is_busy ... ok
test store::tests::review_thousand_authority_changes_per_kind ... ok

test result: ok. 86 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 76.71s

   Doc-tests calternal_auth

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s


cargo clippy -p calternal-db --all-targets -- -D warnings

    Blocking waiting for file lock on build directory
   Compiling syn v3.0.6
    Checking smallvec v1.16.1
   Compiling generic-array v0.14.9
   Compiling syn v2.0.119
   Compiling num-traits v0.2.19
   Compiling serde_core v1.0.229
    Checking futures-sink v0.3.34
   Compiling synstructure v0.14.0
   Compiling zerovec-derive v0.11.6
   Compiling displaydoc v0.2.7
   Compiling zerofrom-derive v0.1.8
   Compiling yoke-derive v0.8.3
   Compiling tokio-macros v2.7.2
    Checking zerofrom v0.1.8
    Checking parking_lot_core v0.9.12
    Checking crypto-common v0.1.6
    Checking block-buffer v0.10.4
   Compiling serde v1.0.229
    Checking futures-util v0.3.34
    Checking tokio v1.53.1
    Checking digest v0.10.7
    Checking yoke v0.8.3
    Checking parking_lot v0.12.5
   Compiling tracing-attributes v0.1.31
    Checking zerovec v0.11.8
    Checking zerotrie v0.2.5
   Compiling serde_derive v1.0.229
    Checking tinystr v0.8.4
    Checking potential_utf v0.1.6
    Checking icu_locale_core v2.3.0
    Checking icu_collections v2.3.0
    Checking icu_provider v2.3.1
   Compiling thiserror-impl v2.0.21
    Checking tracing v0.1.44
    Checking icu_normalizer v2.3.0
    Checking icu_properties v2.3.0
    Checking indexmap v2.14.2
    Checking sha2 v0.10.9
    Checking idna_adapter v1.2.2
    Checking idna v1.1.0
    Checking tokio-stream v0.1.19
    Checking thiserror v2.0.21
    Checking url v2.5.8
    Checking futures-intrusive v0.5.0
    Checking either v1.18.0
    Checking base64 v0.22.1
    Checking flume v0.12.0
   Compiling phf_macros v0.11.3
    Checking sqlx-core v0.9.0
    Checking futures-executor v0.3.34
    Checking atoi v2.0.0
    Checking futures-channel v0.3.34
    Checking chrono v0.4.45
   Compiling rustix v1.1.5
   Compiling serde_json v1.0.151
    Checking phf v0.11.3
    Checking sqlx-sqlite v0.9.0
    Checking linux-raw-sys v0.12.1
    Checking bitflags v2.13.2
    Checking cron v0.17.0
    Checking sqlx v0.9.0
    Checking chrono-tz v0.10.4
    Checking uuid v1.26.1
    Checking fastrand v2.5.0
    Checking tempfile v3.27.0
    Checking calternal-db v0.1.0 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-db)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 3m 29s

cargo test -p calternal-db

    Blocking waiting for file lock on build directory
   Compiling zerofrom v0.1.8
   Compiling smallvec v1.16.1
   Compiling generic-array v0.14.9
   Compiling futures-sink v0.3.34
   Compiling yoke v0.8.3
   Compiling num-traits v0.2.19
   Compiling serde_core v1.0.229
   Compiling block-buffer v0.10.4
   Compiling zerovec v0.11.8
   Compiling zerotrie v0.2.5
   Compiling crypto-common v0.1.6
   Compiling parking_lot_core v0.9.12
   Compiling futures-util v0.3.34
   Compiling tinystr v0.8.4
   Compiling potential_utf v0.1.6
   Compiling parking_lot v0.12.5
   Compiling icu_locale_core v2.3.0
   Compiling icu_collections v2.3.0
   Compiling tokio v1.53.1
   Compiling digest v0.10.7
   Compiling icu_provider v2.3.1
   Compiling indexmap v2.14.2
   Compiling sha2 v0.10.9
   Compiling icu_normalizer v2.3.0
   Compiling icu_properties v2.3.0
   Compiling tracing v0.1.44
   Compiling thiserror v2.0.21
   Compiling tokio-stream v0.1.19
   Compiling serde v1.0.229
   Compiling idna_adapter v1.2.2
   Compiling futures-intrusive v0.5.0
   Compiling base64 v0.22.1
   Compiling idna v1.1.0
   Compiling either v1.18.0
   Compiling chrono v0.4.45
   Compiling flume v0.12.0
   Compiling phf_macros v0.11.3
   Compiling url v2.5.8
   Compiling futures-executor v0.3.34
   Compiling sqlx-core v0.9.0
   Compiling atoi v2.0.0
   Compiling futures-channel v0.3.34
   Compiling linux-raw-sys v0.12.1
   Compiling bitflags v2.13.2
   Compiling phf v0.11.3
   Compiling serde_json v1.0.151
   Compiling sqlx-sqlite v0.9.0
   Compiling cron v0.17.0
   Compiling rustix v1.1.5
   Compiling chrono-tz v0.10.4
   Compiling uuid v1.26.1
   Compiling sqlx v0.9.0
   Compiling fastrand v2.5.0
   Compiling tempfile v3.27.0
   Compiling calternal-db v0.1.0 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-db)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 1m 32s
     Running unittests src/lib.rs (/mnt/hdd/targets/jobs/apw-cache-review/debug/deps/calternal_db-6ce6e6e1ec45335b)

running 12 tests
test db::tests::default_read_pool_covers_fifty_dav_clients ... ok
test cron::tests::zoned_cron_keeps_its_wall_clock_time_across_daylight_saving ... ok
test secrets::tests::named_secret_can_be_replaced_and_cleared_without_reading_it_for_status ... ok
test secrets::tests::named_secret_is_stable_and_first_candidate_wins ... ok
test secrets::tests::rejects_invalid_names_and_empty_candidates ... ok
test secrets::tests::secret_survives_database_reopen ... ok
test sqlite::tests::saturated_or_closed_sqlite_pools_are_transient_service_errors ... ok
test sqlite::tests::wrapped_sqlite_contention_retries_the_whole_operation ... ok
test sqlite::tests::persistent_wrapped_sqlite_contention_stops_at_the_attempt_limit ... ok
test worker::tests::disabled_handler_keeps_jobs_pending_and_finishes_active_work ... ok
test jobs::contention_tests::background_queue_write_waits_past_five_seconds_for_the_writer ... ok
test db::tests::reader_connections_see_a_thousand_immediate_writer_commits ... ok

test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 10.66s

     Running tests/queue.rs (/mnt/hdd/targets/jobs/apw-cache-review/debug/deps/queue-ca91ae55984806aa)

running 17 tests
test enqueue_lease_throughput_microbenchmark ... ignored, manual enqueue plus lease throughput measurement
test cron_enqueues_each_occurrence_once_and_keeps_one_active_job ... ok
test deduplicates_pending_and_leased_jobs ... ok
test expired_lease_is_recovered_and_old_owner_loses_lease ... ok
test controlled_worker_observes_stop_at_handler_checkpoint ... ok
test failure_backoff_uses_fake_clock_and_dead_letters_at_limit ... ok
test job_list_summaries_do_not_read_handler_payloads ... ok
test opens_wal_database_with_required_pragmas ... ok
test plugin_migrations_share_namespaces_and_check_applied_sql ... ok
test queue_change_subscribers_receive_a_hint_after_state_changes ... ok
test heartbeat_does_not_deadlock_a_handler_inside_a_write_transaction ... ok
test owned_job_progress_is_private_and_cancellation_finishes_at_checkpoint ... ok
test queue_retry_run_now_and_clear_only_change_failed_jobs_of_one_kind ... ok
test worker_publishes_registered_kind_metadata_to_the_shared_queue ... ok
test queue_pause_is_idempotent_persistent_and_blocks_new_leases ... ok
test snapshot_restores_as_a_readable_database ... ok
test workers_do_not_execute_a_job_twice ... ok

test result: ok. 16 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.86s

   Doc-tests calternal_db

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s


cargo clippy -p calternal-server --all-targets -- -D warnings

    Checking calternal-auth v0.1.0 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-auth)
   Compiling calternal-server v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-server)
    Checking calternal-plugin-files v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/files)
    Checking calternal-collab v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-collab)
    Checking calternal-plugin-calendar v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/calendar)
    Checking calternal-plugin-ai v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/ai)
    Checking calternal-plugin-photos v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/photos)
    Checking calternal-plugin-video v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/video)
    Checking calternal-plugin-notifications v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/notifications)
    Checking calternal-plugin-analytics v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/analytics)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 42.49s

cargo test -p calternal-server

   Compiling tokio v1.53.1
   Compiling tokio-stream v0.1.19
   Compiling webauthn-authenticator-rs v0.5.5
   Compiling num-derive v0.4.2
   Compiling calternal-server v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-server)
   Compiling sqlx-core v0.9.0
   Compiling tokio-util v0.7.19
   Compiling tower v0.5.3
   Compiling h2 v0.4.19
   Compiling tokio-rustls v0.26.6
   Compiling sqlx-sqlite v0.9.0
   Compiling hyper v1.11.1
   Compiling tokio-tungstenite v0.29.0
   Compiling sqlx-macros-core v0.9.0
   Compiling tower-http v0.6.11
   Compiling hyper-util v0.1.20
   Compiling sqlx-macros v0.9.0
   Compiling calternal-fs v0.1.0 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-fs)
   Compiling axum v0.8.9
   Compiling hyper-rustls v0.27.10
   Compiling reqwest v0.12.28
   Compiling dav-server v0.11.0
   Compiling calternal-location v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-location)
   Compiling oauth2 v5.0.0
   Compiling reqwest v0.13.5
   Compiling calternal-dav v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-dav)
   Compiling openidconnect v4.0.1
   Compiling tokio-native-tls v0.3.1
   Compiling hyper v0.14.32
   Compiling hyperlocal v0.9.1
   Compiling bollard v0.21.1
   Compiling hyper-tls v0.5.0
   Compiling web-push v0.11.0
   Compiling sqlx v0.9.0
   Compiling calternal-db v0.1.0 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-db)
   Compiling async-imap v0.11.3 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/mail/vendor/async-imap)
   Compiling rmcp v3.5.0
   Compiling calternal-plugin v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-plugin)
   Compiling calternal-auth v0.1.0 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-auth)
   Compiling calternal-tags v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-tags)
   Compiling calternal-embed v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-embed)
   Compiling calternal-plugin-notes v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/notes)
   Compiling calternal-plugin-mail v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/mail)
   Compiling calternal-search v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-search)
   Compiling calternal-plugin-money v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/money)
   Compiling calternal-plugin-files v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/files)
   Compiling calternal-collab v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-collab)
   Compiling calternal-plugin-calendar v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/calendar)
   Compiling calternal-plugin-photos v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/photos)
   Compiling calternal-plugin-ai v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/ai)
   Compiling calternal-plugin-video v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/video)
   Compiling calternal-plugin-analytics v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/analytics)
   Compiling calternal-plugin-notifications v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/notifications)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 4m 34s
     Running unittests src/main.rs (/mnt/hdd/targets/jobs/apw-cache-review/debug/deps/calternal_server-3ae677f030818138)

running 98 tests
test appearance::tests::curated_font_ids_deserialize_for_every_role ... ok
test appearance::tests::defaults_and_background_shape_preserve_future_dark_pair ... ok
test appearance::tests::anonymous_requests_get_401_not_500 ... ok
test appearance::tests::disabled_unsplash_and_traversal_never_contact_the_upstream ... ok
test appearance::tests::family_gradients_are_bounded_and_keep_distinct_schemes ... ok
test appearance::tests::auto_scheme_settings_store_only_the_colour_scheme ... ok
test appearance::tests::concurrent_puts_preserve_other_settings ... ok
test appearance::tests::legacy_mono_default_migrates_and_new_spline_choice_stays_explicit ... ok
test appearance::tests::outbound_origins_reject_redirect_hosts_and_userinfo ... ok
test appearance::tests::pair_validates_color_and_image_display_range ... ok
test appearance::tests::font_preferences_persist_and_merge_with_background_updates ... ok
test appearance::tests::search_is_cached_briefly_and_rate_limited_per_user ... ok
test appearance::tests::search_text_and_photo_ids_reject_control_and_path_syntax ... ok
test appearance::tests::setting_family_backgrounds_removes_legacy_pair_once ... ok
test appearance::tests::family_assignment_persists_only_its_family_and_scheme ... ok
test appearance::tests::legacy_background_migration_preserves_saved_appearance_reference ... ok
test appearance::tests::search_rejects_mocked_image_and_api_ssrf_urls ... ok
test authz::tests::distinguishes_anonymous_and_non_admin_before_body_extraction ... ok
test authz::tests::read_guard_does_not_require_a_recent_assertion ... ok
test authz::tests::stale_admin_gets_the_step_up_response_before_body_extraction ... ok
test head::tests::app_html_carries_every_rewritten_tag ... ok
test head::tests::default_shell_gets_absolute_images_and_keeps_the_title ... ok
test head::tests::hostile_names_are_escaped_and_cleaned ... ok
test head::tests::long_names_are_capped_on_a_character_boundary ... ok
test head::tests::origin_is_escaped ... ok
test head::tests::protected_and_unknown_links_get_the_generic_title ... ok
test location::tests::exact_position_suggests_the_local_timezone_without_an_external_lookup ... ok
test appearance::tests::unsplash_is_proxied_downloaded_tracked_and_saved_once ... ok
test location::tests::invalid_coordinates_and_oversized_places_files_are_rejected ... ok
test appearance::tests::visible_uploads_and_library_backgrounds_follow_source_rules ... ok
test location::tests::user_settings_quota_errors_return_insufficient_storage ... ok
test security::tests::api_and_dav_default_to_private_no_store_and_keep_route_policies ... ok
test security::tests::api_gets_a_no_script_policy_and_handlers_keep_their_own ... ok
test security::tests::built_shell_inline_scripts_are_all_hashed ... ok
test security::tests::every_response_has_the_baseline ... ok
test security::tests::shell_policy_hashes_each_inline_script_only ... ok
test security::tests::the_shell_enforces_its_hashed_script_policy ... ok
test appearance::tests::uploaded_image_backgrounds_must_be_decodable_files_in_the_backgrounds_folder ... ok
test serve::tests::forwarded_client_stream_cap_is_shared_and_returns_retry_after ... ok
test serve::tests::direct_untrusted_peer_keeps_the_256_connection_cap ... ok
test serve::tests::per_ip_connection_cap_rejects_excess_and_releases_slots ... ok
test location::tests::saved_places_are_available_only_to_their_user ... ok
test location::tests::legacy_auto_location_moves_to_the_location_setting_without_losing_precision ... ok
test serve::tests::header_timeout_does_not_limit_a_slow_request_body ... ok
test serve::tests::silent_partial_and_idle_connections_are_closed ... ok
test tests::missing_search_query_uses_error_envelope ... ok
test serve::tests::trusted_proxy_connection_lease_survives_a_websocket_upgrade ... ok
test serve::tests::trusted_proxy_accepts_more_than_the_direct_peer_cap ... ok
test tests::plugin_routes_enforce_roles_freshness_core_lock_dependencies_and_user_state ... ok
test tests::reference_system_plugin_returns_instance_info ... ok
test tests::search_can_return_keyword_hits_without_waiting_for_semantics ... ok
test tests::search_fanout_caps_each_provider_and_drops_duplicate_routes ... ok
test tests::search_fanout_returns_fast_results_before_slow_provider_deadline ... ok
test tests::search_open_route_scopes_records_to_the_search_provider ... ok
test tests::search_parse_route_rejects_malformed_operators ... ok
test tests::search_parse_route_uses_the_shared_operator_grammar ... ok
test tests::search_provider_receives_authenticated_permission_context ... ok
test tests::openapi_contains_reference_plugin_and_search_paths ... ok
test tests::search_results_require_data_scope_but_query_parsing_is_public ... ok
test tests::search_route_always_hides_hidden_and_internal_paths ... ok
test tests::search_route_uses_hybrid_by_default_and_accepts_keyword_only_mode ... ok
test tests::unavailable_responses_include_a_retry_hint ... ok
test tests::user_data_dir_defaults_to_data_dir_and_accepts_an_override ... ok
test tests::write_trace_classifies_mutation_routes_without_logging_paths ... ok
test wire::config_watch_tests::admin_config_returns_a_readable_schedule_without_cron ... ok
test wire::config_watch_tests::friendly_schedule_fields_become_internal_wall_clock_cron ... ok
test wire::config_watch_tests::invalid_scrub_schedule_is_rejected ... ok
test wire::config_watch_tests::old_instance_config_keeps_unsigned_profile_delivery ... ok
test wire::config_watch_tests::only_config_edits_reload_the_config ... ok
test wire::config_watch_tests::persisted_zero_default_quota_migrates_to_five_decimal_gigabytes ... ok
test tests::search_route_rejects_an_out_of_range_limit ... ok
test wire::config_watch_tests::profile_signing_paths_must_stay_below_secrets ... ok
test tests::openapi_declares_known_rate_limit_responses ... ok
test wire::config_watch_tests::scrub_schedule_defaults_for_old_config_and_runs_at_low_priority ... ok
test wire::config_watch_tests::profile_signing_refuses_a_key_readable_by_group_or_others ... ok
test wire::dav_settings_error_tests::quota_write_failures_map_to_dav_insufficient_storage ... ok
test wire::mcp::tests::mail_reader_inputs_reject_cursed_ids_categories_and_limits ... ok
test wire::mcp::tests::mcp_tool_access_does_not_expand_the_app_password_scope ... ok
test wire::config_watch_tests::scrub_status_view_uses_stable_file_links_without_storage_terms ... ok
test wire::tests::app_password_route_checks_keep_protocol_and_access_boundaries ... ok
test wire::mcp::tests::query_uri_encodes_unicode_and_cursed_path_separators ... ok
test wire::tests::config_storage_busy_errors_are_retryable ... ok
test wire::tests::first_start_snapshot_contains_the_pre_migration_index ... ignored, builds a live app with process-global plugin state; run by live_apps_run_in_separate_processes
test wire::tests::full_app_setup_session_config_and_backup ... ignored, builds a live app with process-global plugin state; run by live_apps_run_in_separate_processes
test wire::tests::app_surface_flags_are_per_user_with_instance_override ... ok
test wire::tests::mcp_read_and_write_scopes_can_reach_streamable_http_post ... ok
test wire::tests::money_plugin_scope_limits_api_routes_to_selected_access_and_budget ... ok
test wire::tests::public_calendar_feed_path_matches_one_ics_token_segment ... ok
test wire::tests::review_app_password_surface_matrix ... ok
test wire::tests::review_dav_cache_isolation_and_immediate_api_revocation ... ignored, live app and 1000 revocations; run in a separate process
test wire::tests::search_notifications_skip_reserved_home_roots_and_descendants ... ok
test wire::tests::startup_reconciliation_indexes_photos_written_while_stopped ... ignored, builds a live app with process-global plugin state; run by live_apps_run_in_separate_processes
test wire::tests::webdav_app_password_scope_becomes_a_bounded_files_identity ... ok
test wire::tests::webdav_lock_requires_a_replace_capable_scope ... ok
test wire::tests::webdav_write_scope_allows_read_and_safe_writes_only ... ok
test wire::dav_collection_settings_tests::collection_properties_share_user_tag_colors_and_keep_collection_order_separate ... ok
test serve::tests::streaming_response_cannot_hold_shutdown_open ... ok
test wire::tests::live_apps_run_in_separate_processes ... ok

test result: ok. 94 passed; 0 failed; 4 ignored; 0 measured; 0 filtered out; finished in 26.77s


Round 2 complete on `job/apw-cache-review`. Head: `e076d611258627f151fa6be6149e37ae2f6e4194`. Base: `950ade5bcc9e71729d38d9c53e72bfbe6fba113b`. Built: - HMAC-SHA256 positive keys bind server key, User ID, credential ID, scope version and secret. Tests cover different Users, scopes and a version-only cache miss. - Canonical credential flights cap 64 distinct flights, 64 waiters plus one leader per credential, and 256 admitted cold requests. Four password hash jobs can run. Excess admission returns HTTP 429; temporary authority changes return HTTP 503. Both include Retry-After: 1. The 10,000-distinct plus 1,000-same-credential storm passed and a warm valid User still authenticated. - Authority changes unconditionally clear positive entries and fence late writes with a generation counter. The owned commit task holds the change guard even after request cancellation. Demotion, disable, deletion and scope changes retire the old App Password. Four multi-worker tests passed 1,000 transitions each. Secret suffix and UUID case variants share flight limits. Unrelated authority changes return a retry rather than counting against bad-password limits. - The server loads its persisted 32-byte HMAC key once through the existing Index secret store. Server deletion commits use the same authority fence. - The live DAV profile now runs repeated warm/revoke/next-request checks through HTTP. Files: Cargo.lock; crates/calternal-auth/Cargo.toml; crates/calternal-auth/src/store.rs, error.rs, lib.rs; crates/calternal-server/src/wire.rs; bench/app-password-dav.py; docs/perf/runs/2026-10-01-issue-512-app-password-dav.md; docs/perf/runs/2026-10-01-issue-512-round2-app-password-dav.json. No calternal-db source change was needed. Live validation: 50 clients, five requests each per phase, 250 HTTP 207 responses in each phase. Warm p50/p95: 123.027/150.034 ms; cold-start p50/p95: 88.960/626.574 ms. Warm mean CPU/RSS/peak RSS: 89.65%/153.9 MiB/155.7 MiB; cold: 108.15%/127.4 MiB/139.4 MiB. Host load: 11.47/12.77/19.26. All 1,000 live cycles returned warm 207, revoke 204, next DAV 401. The one scripted WebDAV adversarial round passed. Round 1 warm p50/p95 was 218/315 ms; cold was 224/856 ms. Neither p95 nor peak RSS crosses its published regression budget. docs/perf/baseline.json has no comparable App Password DAV profile. These are local debug measurements because the perf VM lock was busy. Decisions: - Use the existing persisted key slot auth.app-password-cache.v1. A missing key is generated and persisted; production restarts keep it. Invalid size stops startup. Standalone/test stores use random ephemeral keys. - Encode scope version as the mutation counter plus SHA256 of the scope restrictions, including Home prefix and Plugin scope. Fixed-width UUIDs and counter avoid ambiguous concatenation. - Retire old credentials after demotion, disable or scope change to meet the required next-request failure. A User issues a new App Password afterward. The scope-change API is a trusted store hook, with no new HTTP endpoint. - Retain the existing instance-wide generation counter. An unrelated change can cause a short retryable 503. - Use the admission limits listed above, a 30-second positive TTL, 4,096 entries and five-second wait deadlines. Known gaps: quiet release measurement on the perf VM remains unavailable. No UI changed. Manual timing/throughput diagnostics remain ignored. The server's ignored live-app tests are exercised by its separate-process wrapper, except the older manual DAV review diagnostic; the new live runner supplies the requested 1,000 HTTP revocation checks. Existing demotion and scope expectations changed only because this issue explicitly requires old credentials to fail on the next request; this was disclosed in the earlier finding comment. The read-only reviewer found no remaining concrete critical or important finding after the fixes. Integration: fetched origin/dev once and merged once; output was `Already up to date.` No push, deploy or merge into dev/main. Module and changed function comments were read again. cargo clean removed 16,371 files / 8.5 GiB; web build output and Python caches were deleted. Working tree is clean. Gate output verbatim follows. All commands exited 0. Environment: CARGO_PROFILE_DEV_DEBUG=line-tables-only, CARGO_INCREMENTAL=0, CARGO_BUILD_JOBS=4, RUST_TEST_THREADS=4, TMPDIR=<worktree>/target/tmp. CARGO_TARGET_DIR was not changed. cargo fmt --check: no output; exit 0. cargo clippy -p calternal-auth --all-targets -- -D warnings ```text Checking calternal-auth v0.1.0 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-auth) Finished `dev` profile [unoptimized + debuginfo] target(s) in 42.95s ``` cargo test -p calternal-auth ```text Compiling calternal-auth v0.1.0 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-auth) Finished `test` profile [unoptimized + debuginfo] target(s) in 18.28s Running unittests src/lib.rs (/mnt/hdd/targets/jobs/apw-cache-review/debug/deps/calternal_auth-8c6bf3e6451def45) running 87 tests test api::cli_login::tests::redirect_is_loopback_only ... ok test api::cli_login::tests::approval_requires_a_browser_cookie_without_bearer_override ... ok test api::cli_login::tests::signed_out_browser_gets_a_sign_in_page_not_a_json_error ... ok test api::tests::apple_profile_is_a_valid_plist_with_the_cal_dav_payload ... ok test api::tests::agent_token_cannot_extract_account_authority ... ok test api::cli_login::tests::device_code_is_one_time_and_bound_to_verifier_and_web_user ... ok test api::tests::admin_lists_need_admin_scope_and_revocation_needs_fresh_assertion ... ok test api::tests::ask_agent_scope_allows_reads_and_denies_write_methods ... ok test api::tests::auth_options_report_setup_and_signup_without_a_session ... ok test api::tests::forwarded_chain_uses_first_untrusted_hop_from_right ... ok test api::tests::authority_routes_require_recent_assertion_on_same_session ... ok test api::tests::profile_tokens_expire_and_are_consumed_once ... ok test api::tests::rate_limit_isolated_by_peer_ip ... ok test api::tests::extractor_keeps_cookie_and_bearer_session_kinds_separate ... ok test api::tests::session_cookie_outlives_the_browser_session ... ok test error::tests::bounded_admission_has_retry_after ... ok test api::tests::passkey_rename_security_summary_and_current_session ... ok test oidc::tests::groups_authoritative_defaults_to_true_in_provider_config ... ok test api::tests::sign_out_revokes_only_the_calling_session_without_fresh_assertion ... ok test oidc::tests::existing_identity_reconciles_groups_by_default ... ok test oidc::tests::non_authoritative_groups_only_set_initial_role ... ok test api::tests::browser_callback_finishes_only_in_the_starting_browser ... ok test passkey::tests::existing_registration_fails_after_initiating_session_revoked ... ok test passkey::tests::setup_registration_then_simulated_usernameless_login ... ok test profile_signing::tests::cms_profile_is_attached_der_sha256_and_contains_the_chain ... ok test profile_signing::tests::expired_signing_certificate_is_rejected ... ok test profile_signing::tests::status_hides_the_certificate_team_id ... ok test profile_signing::tests::status_warns_when_the_chain_expires_within_thirty_days ... ok test profile_signing::tests::unsigned_fallback_preserves_the_profile_bytes ... ok test recovery::tests::checksum_and_normalization ... ok test oidc::tests::oidc_reauth_marks_only_initiating_session ... ok test store::tests::admin_reenrol_link_is_single_use ... ok test store::tests::agent_token_has_data_scope_and_home_shares_limit ... ok test store::tests::all_auth_migrations_run_in_order_on_an_empty_database ... ok test store::tests::app_password_cache_coalesces_concurrent_misses ... ok test store::tests::app_password_cache_rejects_expired_completion_and_hit ... ok test store::tests::app_password_cache_rejects_in_flight_authority ... ok test store::tests::app_password_cache_rejects_late_verification_after_invalidation ... ok test store::tests::app_password_cancellation_keeps_blocking_work_bounded ... ok test store::tests::app_password_digest_maps_preserve_constant_time_equality ... ok test store::tests::account_security_counts_legacy_codes_and_lists_issuers ... ok test store::tests::app_password_options_validate_protocols_and_home_prefixes ... ok test store::tests::app_password_revoke_rejects_queued_verification ... ok test store::tests::app_password_scope_migration_preserves_existing_caldav_rights ... ok test store::tests::disabling_user_invalidates_app_password_authority ... ok test store::tests::display_names_refuse_bidi_and_control_characters ... ok test store::tests::human_session_scopes_follow_role ... ok test store::tests::app_password_is_one_time_secret_bound_to_user_and_revocable ... ok test store::tests::invites_list_without_tokens_and_revoke_only_unconsumed ... ok test store::tests::migration_revokes_unclassified_pre_scope_sessions ... ok test store::tests::oidc_cannot_claim_first_user_or_owner_role ... ok test store::tests::oidc_reconcile_downgrades_and_preserves_owner ... ok test store::tests::pool_acquisition_timeout_is_unavailable_not_internal ... ok test store::tests::profile_role_and_disable_changes_take_effect ... ok test store::tests::quota_overrides_follow_invites_and_require_an_admin ... ok test store::tests::records_an_admin_action_in_security_events ... ok test oidc::tests::groups_claim_maps_admin_and_guest ... ok test store::tests::key_rotation_revokes_sessions_and_rejects_competing_recovery ... ok test store::tests::rename_passkey_is_owner_only_and_validates_the_label ... ok test store::tests::recovery_code_is_one_time ... ok test oidc::tests::validates_nonce_audience_expiry_and_refreshes_rotated_key ... ok test store::tests::recovery_proof_is_checked_before_challenge ... ok test store::tests::review_change_window_rejects_positive_writes ... ok test store::tests::review_demotion_revokes_app_password ... ok test store::tests::review_invalid_credential_timing_distributions ... ignored, timing diagnostic; run once with --ignored --nocapture test store::tests::review_identity_bound_cache_key ... ok test store::tests::review_cache_bounds_ttl_and_keyed_digest ... ok test store::tests::review_round_two_suffixes_share_credential_admission ... ok test store::tests::review_round_two_late_unrelated_change_is_retryable ... ok test store::tests::review_round_two_unrelated_change_is_retryable ... ok test store::tests::review_failed_attempts_and_two_user_cache_matrix ... ok test store::tests::review_mutations_reject_queued_verification ... ok test store::tests::review_waiter_admission_is_bounded ... ok test store::tests::revoke_all_keeps_only_current_and_requires_fresh_for_changes ... ok test store::tests::role_and_disable_revoke_sessions_and_event_failure_rolls_back ... ok test store::tests::review_ten_thousand_credentials_and_thousand_waiters ... ok test store::tests::scope_change_uses_shared_app_password_cache_invalidation ... ok test store::tests::sessions_are_kind_bound_and_revocable ... ok test store::tests::sessions_expire_at_idle_and_absolute_deadlines ... ok test store::tests::setup_is_single_use_under_concurrency ... ok test store::tests::transfer_target_cannot_be_deleted_until_pending_transfer_finishes ... ok test store::tests::unlink_oidc_keeps_a_credential_and_rolls_back_on_audit_failure ... ok test store::tests::user_deletion_revokes_sessions_and_resumes_with_the_original_expiry ... ok test store::tests::username_is_case_insensitive_and_invite_single_use ... ok test store::tests::review_user_and_secret_mutation_matrix ... ok test store::tests::session_authority_answers_while_the_writer_pool_is_busy ... ok test store::tests::review_thousand_authority_changes_per_kind ... ok test result: ok. 86 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 76.71s Doc-tests calternal_auth running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` cargo clippy -p calternal-db --all-targets -- -D warnings ```text Blocking waiting for file lock on build directory Compiling syn v3.0.6 Checking smallvec v1.16.1 Compiling generic-array v0.14.9 Compiling syn v2.0.119 Compiling num-traits v0.2.19 Compiling serde_core v1.0.229 Checking futures-sink v0.3.34 Compiling synstructure v0.14.0 Compiling zerovec-derive v0.11.6 Compiling displaydoc v0.2.7 Compiling zerofrom-derive v0.1.8 Compiling yoke-derive v0.8.3 Compiling tokio-macros v2.7.2 Checking zerofrom v0.1.8 Checking parking_lot_core v0.9.12 Checking crypto-common v0.1.6 Checking block-buffer v0.10.4 Compiling serde v1.0.229 Checking futures-util v0.3.34 Checking tokio v1.53.1 Checking digest v0.10.7 Checking yoke v0.8.3 Checking parking_lot v0.12.5 Compiling tracing-attributes v0.1.31 Checking zerovec v0.11.8 Checking zerotrie v0.2.5 Compiling serde_derive v1.0.229 Checking tinystr v0.8.4 Checking potential_utf v0.1.6 Checking icu_locale_core v2.3.0 Checking icu_collections v2.3.0 Checking icu_provider v2.3.1 Compiling thiserror-impl v2.0.21 Checking tracing v0.1.44 Checking icu_normalizer v2.3.0 Checking icu_properties v2.3.0 Checking indexmap v2.14.2 Checking sha2 v0.10.9 Checking idna_adapter v1.2.2 Checking idna v1.1.0 Checking tokio-stream v0.1.19 Checking thiserror v2.0.21 Checking url v2.5.8 Checking futures-intrusive v0.5.0 Checking either v1.18.0 Checking base64 v0.22.1 Checking flume v0.12.0 Compiling phf_macros v0.11.3 Checking sqlx-core v0.9.0 Checking futures-executor v0.3.34 Checking atoi v2.0.0 Checking futures-channel v0.3.34 Checking chrono v0.4.45 Compiling rustix v1.1.5 Compiling serde_json v1.0.151 Checking phf v0.11.3 Checking sqlx-sqlite v0.9.0 Checking linux-raw-sys v0.12.1 Checking bitflags v2.13.2 Checking cron v0.17.0 Checking sqlx v0.9.0 Checking chrono-tz v0.10.4 Checking uuid v1.26.1 Checking fastrand v2.5.0 Checking tempfile v3.27.0 Checking calternal-db v0.1.0 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-db) Finished `dev` profile [unoptimized + debuginfo] target(s) in 3m 29s ``` cargo test -p calternal-db ```text Blocking waiting for file lock on build directory Compiling zerofrom v0.1.8 Compiling smallvec v1.16.1 Compiling generic-array v0.14.9 Compiling futures-sink v0.3.34 Compiling yoke v0.8.3 Compiling num-traits v0.2.19 Compiling serde_core v1.0.229 Compiling block-buffer v0.10.4 Compiling zerovec v0.11.8 Compiling zerotrie v0.2.5 Compiling crypto-common v0.1.6 Compiling parking_lot_core v0.9.12 Compiling futures-util v0.3.34 Compiling tinystr v0.8.4 Compiling potential_utf v0.1.6 Compiling parking_lot v0.12.5 Compiling icu_locale_core v2.3.0 Compiling icu_collections v2.3.0 Compiling tokio v1.53.1 Compiling digest v0.10.7 Compiling icu_provider v2.3.1 Compiling indexmap v2.14.2 Compiling sha2 v0.10.9 Compiling icu_normalizer v2.3.0 Compiling icu_properties v2.3.0 Compiling tracing v0.1.44 Compiling thiserror v2.0.21 Compiling tokio-stream v0.1.19 Compiling serde v1.0.229 Compiling idna_adapter v1.2.2 Compiling futures-intrusive v0.5.0 Compiling base64 v0.22.1 Compiling idna v1.1.0 Compiling either v1.18.0 Compiling chrono v0.4.45 Compiling flume v0.12.0 Compiling phf_macros v0.11.3 Compiling url v2.5.8 Compiling futures-executor v0.3.34 Compiling sqlx-core v0.9.0 Compiling atoi v2.0.0 Compiling futures-channel v0.3.34 Compiling linux-raw-sys v0.12.1 Compiling bitflags v2.13.2 Compiling phf v0.11.3 Compiling serde_json v1.0.151 Compiling sqlx-sqlite v0.9.0 Compiling cron v0.17.0 Compiling rustix v1.1.5 Compiling chrono-tz v0.10.4 Compiling uuid v1.26.1 Compiling sqlx v0.9.0 Compiling fastrand v2.5.0 Compiling tempfile v3.27.0 Compiling calternal-db v0.1.0 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-db) Finished `test` profile [unoptimized + debuginfo] target(s) in 1m 32s Running unittests src/lib.rs (/mnt/hdd/targets/jobs/apw-cache-review/debug/deps/calternal_db-6ce6e6e1ec45335b) running 12 tests test db::tests::default_read_pool_covers_fifty_dav_clients ... ok test cron::tests::zoned_cron_keeps_its_wall_clock_time_across_daylight_saving ... ok test secrets::tests::named_secret_can_be_replaced_and_cleared_without_reading_it_for_status ... ok test secrets::tests::named_secret_is_stable_and_first_candidate_wins ... ok test secrets::tests::rejects_invalid_names_and_empty_candidates ... ok test secrets::tests::secret_survives_database_reopen ... ok test sqlite::tests::saturated_or_closed_sqlite_pools_are_transient_service_errors ... ok test sqlite::tests::wrapped_sqlite_contention_retries_the_whole_operation ... ok test sqlite::tests::persistent_wrapped_sqlite_contention_stops_at_the_attempt_limit ... ok test worker::tests::disabled_handler_keeps_jobs_pending_and_finishes_active_work ... ok test jobs::contention_tests::background_queue_write_waits_past_five_seconds_for_the_writer ... ok test db::tests::reader_connections_see_a_thousand_immediate_writer_commits ... ok test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 10.66s Running tests/queue.rs (/mnt/hdd/targets/jobs/apw-cache-review/debug/deps/queue-ca91ae55984806aa) running 17 tests test enqueue_lease_throughput_microbenchmark ... ignored, manual enqueue plus lease throughput measurement test cron_enqueues_each_occurrence_once_and_keeps_one_active_job ... ok test deduplicates_pending_and_leased_jobs ... ok test expired_lease_is_recovered_and_old_owner_loses_lease ... ok test controlled_worker_observes_stop_at_handler_checkpoint ... ok test failure_backoff_uses_fake_clock_and_dead_letters_at_limit ... ok test job_list_summaries_do_not_read_handler_payloads ... ok test opens_wal_database_with_required_pragmas ... ok test plugin_migrations_share_namespaces_and_check_applied_sql ... ok test queue_change_subscribers_receive_a_hint_after_state_changes ... ok test heartbeat_does_not_deadlock_a_handler_inside_a_write_transaction ... ok test owned_job_progress_is_private_and_cancellation_finishes_at_checkpoint ... ok test queue_retry_run_now_and_clear_only_change_failed_jobs_of_one_kind ... ok test worker_publishes_registered_kind_metadata_to_the_shared_queue ... ok test queue_pause_is_idempotent_persistent_and_blocks_new_leases ... ok test snapshot_restores_as_a_readable_database ... ok test workers_do_not_execute_a_job_twice ... ok test result: ok. 16 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.86s Doc-tests calternal_db running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` cargo clippy -p calternal-server --all-targets -- -D warnings ```text Checking calternal-auth v0.1.0 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-auth) Compiling calternal-server v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-server) Checking calternal-plugin-files v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/files) Checking calternal-collab v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-collab) Checking calternal-plugin-calendar v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/calendar) Checking calternal-plugin-ai v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/ai) Checking calternal-plugin-photos v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/photos) Checking calternal-plugin-video v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/video) Checking calternal-plugin-notifications v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/notifications) Checking calternal-plugin-analytics v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/analytics) Finished `dev` profile [unoptimized + debuginfo] target(s) in 42.49s ``` cargo test -p calternal-server ```text Compiling tokio v1.53.1 Compiling tokio-stream v0.1.19 Compiling webauthn-authenticator-rs v0.5.5 Compiling num-derive v0.4.2 Compiling calternal-server v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-server) Compiling sqlx-core v0.9.0 Compiling tokio-util v0.7.19 Compiling tower v0.5.3 Compiling h2 v0.4.19 Compiling tokio-rustls v0.26.6 Compiling sqlx-sqlite v0.9.0 Compiling hyper v1.11.1 Compiling tokio-tungstenite v0.29.0 Compiling sqlx-macros-core v0.9.0 Compiling tower-http v0.6.11 Compiling hyper-util v0.1.20 Compiling sqlx-macros v0.9.0 Compiling calternal-fs v0.1.0 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-fs) Compiling axum v0.8.9 Compiling hyper-rustls v0.27.10 Compiling reqwest v0.12.28 Compiling dav-server v0.11.0 Compiling calternal-location v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-location) Compiling oauth2 v5.0.0 Compiling reqwest v0.13.5 Compiling calternal-dav v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-dav) Compiling openidconnect v4.0.1 Compiling tokio-native-tls v0.3.1 Compiling hyper v0.14.32 Compiling hyperlocal v0.9.1 Compiling bollard v0.21.1 Compiling hyper-tls v0.5.0 Compiling web-push v0.11.0 Compiling sqlx v0.9.0 Compiling calternal-db v0.1.0 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-db) Compiling async-imap v0.11.3 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/mail/vendor/async-imap) Compiling rmcp v3.5.0 Compiling calternal-plugin v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-plugin) Compiling calternal-auth v0.1.0 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-auth) Compiling calternal-tags v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-tags) Compiling calternal-embed v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-embed) Compiling calternal-plugin-notes v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/notes) Compiling calternal-plugin-mail v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/mail) Compiling calternal-search v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-search) Compiling calternal-plugin-money v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/money) Compiling calternal-plugin-files v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/files) Compiling calternal-collab v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/calternal-collab) Compiling calternal-plugin-calendar v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/calendar) Compiling calternal-plugin-photos v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/photos) Compiling calternal-plugin-ai v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/ai) Compiling calternal-plugin-video v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/video) Compiling calternal-plugin-analytics v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/analytics) Compiling calternal-plugin-notifications v0.0.1 (/home/kayg/Developer/calternal-wt/apw-cache-review/crates/plugins/notifications) Finished `test` profile [unoptimized + debuginfo] target(s) in 4m 34s Running unittests src/main.rs (/mnt/hdd/targets/jobs/apw-cache-review/debug/deps/calternal_server-3ae677f030818138) running 98 tests test appearance::tests::curated_font_ids_deserialize_for_every_role ... ok test appearance::tests::defaults_and_background_shape_preserve_future_dark_pair ... ok test appearance::tests::anonymous_requests_get_401_not_500 ... ok test appearance::tests::disabled_unsplash_and_traversal_never_contact_the_upstream ... ok test appearance::tests::family_gradients_are_bounded_and_keep_distinct_schemes ... ok test appearance::tests::auto_scheme_settings_store_only_the_colour_scheme ... ok test appearance::tests::concurrent_puts_preserve_other_settings ... ok test appearance::tests::legacy_mono_default_migrates_and_new_spline_choice_stays_explicit ... ok test appearance::tests::outbound_origins_reject_redirect_hosts_and_userinfo ... ok test appearance::tests::pair_validates_color_and_image_display_range ... ok test appearance::tests::font_preferences_persist_and_merge_with_background_updates ... ok test appearance::tests::search_is_cached_briefly_and_rate_limited_per_user ... ok test appearance::tests::search_text_and_photo_ids_reject_control_and_path_syntax ... ok test appearance::tests::setting_family_backgrounds_removes_legacy_pair_once ... ok test appearance::tests::family_assignment_persists_only_its_family_and_scheme ... ok test appearance::tests::legacy_background_migration_preserves_saved_appearance_reference ... ok test appearance::tests::search_rejects_mocked_image_and_api_ssrf_urls ... ok test authz::tests::distinguishes_anonymous_and_non_admin_before_body_extraction ... ok test authz::tests::read_guard_does_not_require_a_recent_assertion ... ok test authz::tests::stale_admin_gets_the_step_up_response_before_body_extraction ... ok test head::tests::app_html_carries_every_rewritten_tag ... ok test head::tests::default_shell_gets_absolute_images_and_keeps_the_title ... ok test head::tests::hostile_names_are_escaped_and_cleaned ... ok test head::tests::long_names_are_capped_on_a_character_boundary ... ok test head::tests::origin_is_escaped ... ok test head::tests::protected_and_unknown_links_get_the_generic_title ... ok test location::tests::exact_position_suggests_the_local_timezone_without_an_external_lookup ... ok test appearance::tests::unsplash_is_proxied_downloaded_tracked_and_saved_once ... ok test location::tests::invalid_coordinates_and_oversized_places_files_are_rejected ... ok test appearance::tests::visible_uploads_and_library_backgrounds_follow_source_rules ... ok test location::tests::user_settings_quota_errors_return_insufficient_storage ... ok test security::tests::api_and_dav_default_to_private_no_store_and_keep_route_policies ... ok test security::tests::api_gets_a_no_script_policy_and_handlers_keep_their_own ... ok test security::tests::built_shell_inline_scripts_are_all_hashed ... ok test security::tests::every_response_has_the_baseline ... ok test security::tests::shell_policy_hashes_each_inline_script_only ... ok test security::tests::the_shell_enforces_its_hashed_script_policy ... ok test appearance::tests::uploaded_image_backgrounds_must_be_decodable_files_in_the_backgrounds_folder ... ok test serve::tests::forwarded_client_stream_cap_is_shared_and_returns_retry_after ... ok test serve::tests::direct_untrusted_peer_keeps_the_256_connection_cap ... ok test serve::tests::per_ip_connection_cap_rejects_excess_and_releases_slots ... ok test location::tests::saved_places_are_available_only_to_their_user ... ok test location::tests::legacy_auto_location_moves_to_the_location_setting_without_losing_precision ... ok test serve::tests::header_timeout_does_not_limit_a_slow_request_body ... ok test serve::tests::silent_partial_and_idle_connections_are_closed ... ok test tests::missing_search_query_uses_error_envelope ... ok test serve::tests::trusted_proxy_connection_lease_survives_a_websocket_upgrade ... ok test serve::tests::trusted_proxy_accepts_more_than_the_direct_peer_cap ... ok test tests::plugin_routes_enforce_roles_freshness_core_lock_dependencies_and_user_state ... ok test tests::reference_system_plugin_returns_instance_info ... ok test tests::search_can_return_keyword_hits_without_waiting_for_semantics ... ok test tests::search_fanout_caps_each_provider_and_drops_duplicate_routes ... ok test tests::search_fanout_returns_fast_results_before_slow_provider_deadline ... ok test tests::search_open_route_scopes_records_to_the_search_provider ... ok test tests::search_parse_route_rejects_malformed_operators ... ok test tests::search_parse_route_uses_the_shared_operator_grammar ... ok test tests::search_provider_receives_authenticated_permission_context ... ok test tests::openapi_contains_reference_plugin_and_search_paths ... ok test tests::search_results_require_data_scope_but_query_parsing_is_public ... ok test tests::search_route_always_hides_hidden_and_internal_paths ... ok test tests::search_route_uses_hybrid_by_default_and_accepts_keyword_only_mode ... ok test tests::unavailable_responses_include_a_retry_hint ... ok test tests::user_data_dir_defaults_to_data_dir_and_accepts_an_override ... ok test tests::write_trace_classifies_mutation_routes_without_logging_paths ... ok test wire::config_watch_tests::admin_config_returns_a_readable_schedule_without_cron ... ok test wire::config_watch_tests::friendly_schedule_fields_become_internal_wall_clock_cron ... ok test wire::config_watch_tests::invalid_scrub_schedule_is_rejected ... ok test wire::config_watch_tests::old_instance_config_keeps_unsigned_profile_delivery ... ok test wire::config_watch_tests::only_config_edits_reload_the_config ... ok test wire::config_watch_tests::persisted_zero_default_quota_migrates_to_five_decimal_gigabytes ... ok test tests::search_route_rejects_an_out_of_range_limit ... ok test wire::config_watch_tests::profile_signing_paths_must_stay_below_secrets ... ok test tests::openapi_declares_known_rate_limit_responses ... ok test wire::config_watch_tests::scrub_schedule_defaults_for_old_config_and_runs_at_low_priority ... ok test wire::config_watch_tests::profile_signing_refuses_a_key_readable_by_group_or_others ... ok test wire::dav_settings_error_tests::quota_write_failures_map_to_dav_insufficient_storage ... ok test wire::mcp::tests::mail_reader_inputs_reject_cursed_ids_categories_and_limits ... ok test wire::mcp::tests::mcp_tool_access_does_not_expand_the_app_password_scope ... ok test wire::config_watch_tests::scrub_status_view_uses_stable_file_links_without_storage_terms ... ok test wire::tests::app_password_route_checks_keep_protocol_and_access_boundaries ... ok test wire::mcp::tests::query_uri_encodes_unicode_and_cursed_path_separators ... ok test wire::tests::config_storage_busy_errors_are_retryable ... ok test wire::tests::first_start_snapshot_contains_the_pre_migration_index ... ignored, builds a live app with process-global plugin state; run by live_apps_run_in_separate_processes test wire::tests::full_app_setup_session_config_and_backup ... ignored, builds a live app with process-global plugin state; run by live_apps_run_in_separate_processes test wire::tests::app_surface_flags_are_per_user_with_instance_override ... ok test wire::tests::mcp_read_and_write_scopes_can_reach_streamable_http_post ... ok test wire::tests::money_plugin_scope_limits_api_routes_to_selected_access_and_budget ... ok test wire::tests::public_calendar_feed_path_matches_one_ics_token_segment ... ok test wire::tests::review_app_password_surface_matrix ... ok test wire::tests::review_dav_cache_isolation_and_immediate_api_revocation ... ignored, live app and 1000 revocations; run in a separate process test wire::tests::search_notifications_skip_reserved_home_roots_and_descendants ... ok test wire::tests::startup_reconciliation_indexes_photos_written_while_stopped ... ignored, builds a live app with process-global plugin state; run by live_apps_run_in_separate_processes test wire::tests::webdav_app_password_scope_becomes_a_bounded_files_identity ... ok test wire::tests::webdav_lock_requires_a_replace_capable_scope ... ok test wire::tests::webdav_write_scope_allows_read_and_safe_writes_only ... ok test wire::dav_collection_settings_tests::collection_properties_share_user_tag_colors_and_keep_collection_order_separate ... ok test serve::tests::streaming_response_cannot_hold_shutdown_open ... ok test wire::tests::live_apps_run_in_separate_processes ... ok test result: ok. 94 passed; 0 failed; 4 ignored; 0 measured; 0 filtered out; finished in 26.77s ```
Author
Owner

Additional #663 server audit evidence at c4a61e8cf and round 7a 2f4482ded: even after cached verify_app_password, session_context awaits record_app_password_use for DAV and App Password API/MCP calls. The method (auth/src/store.rs:1284 at base, :1778 in 7a) UPDATEs last_used_at/protocol/IP through the single writer pool. Warm protected reads can therefore queue behind unrelated indexing/writes. Please retain this in #512/#587's scope: coalesce optional activity bookkeeping, keep current authority/revocation checks fail-closed through the reader pool, and prove warm reads complete while writer_pool is held. Do not speed this up by skipping current revocation checks. Source evidence only; no new latency measurement.

Additional #663 server audit evidence at c4a61e8cf and round 7a 2f4482ded: even after cached verify_app_password, session_context awaits record_app_password_use for DAV and App Password API/MCP calls. The method (`auth/src/store.rs:1284` at base, :1778 in 7a) UPDATEs last_used_at/protocol/IP through the single writer pool. Warm protected reads can therefore queue behind unrelated indexing/writes. Please retain this in #512/#587's scope: coalesce optional activity bookkeeping, keep current authority/revocation checks fail-closed through the reader pool, and prove warm reads complete while writer_pool is held. Do not speed this up by skipping current revocation checks. Source evidence only; no new latency measurement.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#512
No description provided.