github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/pegboard.go (about) 1 package libkb 2 3 import ( 4 "fmt" 5 "os" 6 "sync" 7 8 keybase1 "github.com/keybase/client/go/protocol/keybase1" 9 ) 10 11 // Pegboard keeps track of automatic private follows. 12 // When the logged-in user interacts with another user, that other user 13 // gets pegged to their incarnation. After the target resets, 14 // the logged-in user will be alerted even if there's no explicit following. 15 // CORE-10522 For now, pegboard is disabled by default and when enabled 16 // 17 // only has in-memory storage. 18 type Pegboard struct { 19 mu sync.Mutex 20 enabled bool 21 store map[keybase1.UID]Peg 22 // CORE-10522 The eventual plan is to store pegs in user's private sigchain 23 // or just leveldb. With an lru in front. 24 } 25 26 // CORE-10522 When a peg breaks, the chat UI of "Let them in" doesn't make sense. 27 28 // CORE-10522 In chat only impteam convs are handled. Have to figure out story for KBFS convs. 29 30 // CORE-10522 In addition to chat, KBFS might should also use pegs. 31 32 // CORE-10522 Looking at a profile should call ObserveUV. 33 34 func NewPegboard() *Pegboard { 35 return &Pegboard{ 36 enabled: os.Getenv("KEYBASE_EXPERIMENT_PEGBOARD") == "1", 37 store: make(map[keybase1.UID]Peg), 38 } 39 } 40 41 type Peg struct { 42 UID keybase1.UID 43 EldestSeqno keybase1.Seqno 44 // CORE-10522 Seqno may not be enough. Consider pegging LinkID. 45 } 46 47 // Update a peg. 48 // The logged-in user has purposefully interacted with this version of the user. 49 func (p *Pegboard) TrackUPAK(mctx MetaContext, upak keybase1.UserPlusKeysV2) (err error) { 50 if !p.enabled { 51 return nil 52 } 53 defer mctx.Trace("Pegboard.TrackUPAK", &err)() 54 p.mu.Lock() 55 defer p.mu.Unlock() 56 if peg, ok := p.store[upak.Uid]; ok && peg.EldestSeqno != 0 && upak.EldestSeqno != 0 && upak.EldestSeqno < peg.EldestSeqno { 57 // User is already tracked. But at a newer version than the argument. 58 // CORE-10522 check with explicit local and remote follows? 59 return fmt.Errorf("Cannot update to older version of user: %v %v < %v", upak.Uid, upak.EldestSeqno, peg.EldestSeqno) 60 } 61 p.store[upak.Uid] = Peg{ 62 UID: upak.Uid, 63 EldestSeqno: upak.EldestSeqno, 64 } 65 return nil 66 } 67 68 // Returns an error if this user has reset since their peg was last udpated. 69 // Pegs users that haven't been seen before. 70 func (p *Pegboard) CheckUV(mctx MetaContext, uv keybase1.UserVersion) error { 71 return p.checkUV(mctx, uv) 72 } 73 74 // Pegs users that haven't been seen before. 75 func (p *Pegboard) ObserveUV(mctx MetaContext, uv keybase1.UserVersion) { 76 _ = p.checkUV(mctx, uv) 77 } 78 79 func (p *Pegboard) checkUV(mctx MetaContext, uv keybase1.UserVersion) error { 80 if !p.enabled { 81 return nil 82 } 83 p.mu.Lock() 84 defer p.mu.Unlock() 85 if peg, ok := p.store[uv.Uid]; ok { 86 // This uid has been seen before. 87 if peg.EldestSeqno == 0 { 88 // Track the new version. 89 p.store[uv.Uid] = Peg{ 90 UID: uv.Uid, 91 EldestSeqno: uv.EldestSeqno, 92 } 93 } else { 94 if uv.EldestSeqno != peg.EldestSeqno { 95 return fmt.Errorf("User version does not match peg %v != %v", uv.EldestSeqno, peg.EldestSeqno) 96 } 97 } 98 } else { 99 // First time seeing this uid. 100 p.store[uv.Uid] = Peg{ 101 UID: uv.Uid, 102 EldestSeqno: uv.EldestSeqno, 103 } 104 } 105 return nil 106 } 107 108 func (p *Pegboard) OnLogout(mctx MetaContext) { 109 if !p.enabled { 110 return 111 } 112 p.mu.Lock() 113 defer p.mu.Unlock() 114 p.store = make(map[keybase1.UID]Peg) 115 }