@@ -273,34 +273,16 @@ pub struct BucketEntry {
273273/// Global users-index CBOR. Master pins one per snapshot; the CID
274274/// is published via IPNS (every flush) and to the chain anchor
275275/// (every 12h).
276- ///
277- /// **Audit F-A3 (issue #15)**: `users_v2` is the additive
278- /// client-derived lookup-key map. When `users_v2` is empty (no Mode B
279- /// users yet), the CBOR byte-shape is identical to the legacy v1
280- /// schema thanks to `skip_serializing_if = "BTreeMap::is_empty"`.
281- /// SDK clients on Mode B prefer `users_v2.get(client_derived_v2_key)`
282- /// and fall back to `users.get(hashed_user_id_v1)` on miss. v1 is
283- /// kept indefinitely per the design decision — never-upgrading
284- /// clients keep working.
285276#[ derive( Clone , Debug , PartialEq , Eq , Serialize , Deserialize ) ]
286277pub struct GlobalUsersIndex {
287278 pub v : u32 ,
288279 /// Monotonic publisher sequence. Replay defense: SDK persists
289280 /// `highest_seen_sequence`; rejects payloads with regression.
290281 pub sequence : u64 ,
291282 pub updated_at_unix : u64 ,
292- /// **v1, kept indefinitely.** `hashed_user_id_hex` (32 hex chars
293- /// = 16-byte `BLAKE3("fula:user_id:" || user_id)[..16]`) →
283+ /// `userKey_hex` (32 hex chars = 16-byte hashed_user_id) →
294284 /// per-user bucketsIndex CID (string). BTreeMap for determinism.
295285 pub users : BTreeMap < String , String > ,
296- /// **v2 client-derived lookup keys** (audit F-A3 / issue #15).
297- /// `client_derived_lookup_key_hex` (32 hex chars = 16-byte
298- /// `BLAKE3("fula:user-lookup-v2:" || user_id || master_KEK_public)[..16]`) →
299- /// per-user bucketsIndex CID. Populated only for users whose
300- /// client has explicitly POSTed a v2 key (Mode B + F-A3-aware
301- /// SDK). Empty by default → CBOR byte-equivalent to legacy v1.
302- #[ serde( default , skip_serializing_if = "BTreeMap::is_empty" ) ]
303- pub users_v2 : BTreeMap < String , String > ,
304286}
305287
306288// ============================================================
@@ -360,46 +342,20 @@ pub fn build_user_buckets_index(
360342
361343/// Build the global users-index CBOR from a per-user CID map.
362344/// `entries` is `userKey_hex (32 hex) → bucketsIndexCid`.
363- ///
364- /// Convenience for the v1-only path; equivalent to
365- /// `build_global_users_index_v2(entries, &BTreeMap::new(), sequence, now_unix)`.
366345pub fn build_global_users_index (
367346 entries : & BTreeMap < String , Cid > ,
368347 sequence : u64 ,
369348 now_unix : u64 ,
370349) -> GlobalUsersIndex {
371- build_global_users_index_v2 ( entries, & BTreeMap :: new ( ) , sequence, now_unix)
372- }
373-
374- /// Build the global users-index CBOR including BOTH v1 and v2 lookup
375- /// maps. `v2_entries` is `client_derived_v2_key_hex → bucketsIndexCid`.
376- ///
377- /// **Audit F-A3 (issue #15)**: when `v2_entries` is empty, the
378- /// resulting CBOR is byte-equivalent to the legacy v1 shape (the
379- /// `users_v2` field is omitted via `skip_serializing_if`).
380- pub fn build_global_users_index_v2 (
381- v1_entries : & BTreeMap < String , Cid > ,
382- v2_entries : & BTreeMap < String , Cid > ,
383- sequence : u64 ,
384- now_unix : u64 ,
385- ) -> GlobalUsersIndex {
386- let users: BTreeMap < String , String > = v1_entries
387- . iter ( )
388- . map ( |( uk, cid) | ( uk. clone ( ) , cid. to_string ( ) ) )
389- . collect ( ) ;
390- let users_v2: BTreeMap < String , String > = v2_entries
350+ let users: BTreeMap < String , String > = entries
391351 . iter ( )
392352 . map ( |( uk, cid) | ( uk. clone ( ) , cid. to_string ( ) ) )
393353 . collect ( ) ;
394354 GlobalUsersIndex {
395- // Bumped to 2 to match the schema change. SDK clients that
396- // know the v2 shape can short-circuit; old clients only read
397- // the `users` field and continue to function.
398- v : if users_v2. is_empty ( ) { 1 } else { 2 } ,
355+ v : 1 ,
399356 sequence,
400357 updated_at_unix : now_unix,
401358 users,
402- users_v2,
403359 }
404360}
405361
0 commit comments