What happens
Two first-time vault setups for the same user, submitted at the same moment, can both succeed. The user then has two active encryption suites. That is the state #173 described: new secrets get sealed to a key the user is not unlocking with, and nothing says so at the time.
A double click on "Set up vault", or two open tabs, is enough to try it.
Why
lib/Service/EncryptionSuiteProvisioningService.php:118 counts the owner's active suites, and persistSuite() inserts afterwards. There is no lock and no unique constraint between the two. The certificate is signed before the count (:107), which widens the window.
The plain double-submit case from #173 is fixed: a second create now gets 409 (d475d00, 2026-08-21). Only the concurrent case is left.
Fix direction
- Add a partial unique index on active suites per owner where the database supports it, or a dedicated per-owner lock row.
- Or take an
ILockingProvider lock on keepiq/suite/<ownerType>/<ownerId> around count and insert.
- Keep the compromise-recovery successor path (
createSuccessorSuite) outside the lock rule on purpose.
Live check
- With a fresh user, send two
POST /apps/keepiq/api/v1/suites requests in parallel (for example two curl calls started with &).
- Call
GET /apps/keepiq/api/v1/suites. If it lists two active suites, the defect holds.
Related: #173 (the sequential case, fixed), #395. Matrix row: crypto-15 in openspec/parity/capabilities.json (development, 06f25bf).
What happens
Two first-time vault setups for the same user, submitted at the same moment, can both succeed. The user then has two active encryption suites. That is the state #173 described: new secrets get sealed to a key the user is not unlocking with, and nothing says so at the time.
A double click on "Set up vault", or two open tabs, is enough to try it.
Why
lib/Service/EncryptionSuiteProvisioningService.php:118counts the owner's active suites, andpersistSuite()inserts afterwards. There is no lock and no unique constraint between the two. The certificate is signed before the count (:107), which widens the window.The plain double-submit case from #173 is fixed: a second create now gets 409 (d475d00, 2026-08-21). Only the concurrent case is left.
Fix direction
ILockingProviderlock onkeepiq/suite/<ownerType>/<ownerId>around count and insert.createSuccessorSuite) outside the lock rule on purpose.Live check
POST /apps/keepiq/api/v1/suitesrequests in parallel (for example twocurlcalls started with&).GET /apps/keepiq/api/v1/suites. If it lists two active suites, the defect holds.Related: #173 (the sequential case, fixed), #395. Matrix row:
crypto-15inopenspec/parity/capabilities.json(development, 06f25bf).