| 1 | package calendar |
| 2 | |
| 3 | import "time" |
| 4 | |
| 5 | // Attendee is somebody named on an event who is not necessarily on this |
| 6 | // team. |
| 7 | // |
| 8 | // RECORDED, NOT INVITED. Nothing in this app emails an attendee. The |
| 9 | // row is a note about who is coming, the event page says so in those |
| 10 | // words, and the ICS export carries it so a calendar that DOES send |
| 11 | // invitations can. Storing this without saying it would let somebody |
| 12 | // type "lunch with doc@example.com", read the word "attendee", and |
| 13 | // reasonably conclude a message went out. |
| 14 | // |
| 15 | // The address is SEALED, and that is a decision rather than a habit. |
| 16 | // AGENTS.md's falsifiable form already gives up "who is on the team" — |
| 17 | // team members are plaintext in `people` and there is no way around |
| 18 | // that while sign-in is by address. An external attendee is not on the |
| 19 | // team, and who this team MEETS WITH is exactly the fact the one rule |
| 20 | // exists to keep out of the bucket. |
| 21 | type Attendee struct { |
| 22 | ID int64 `gorm:"primaryKey"` |
| 23 | EventID int64 `gorm:"column:event_id;not null;index"` |
| 24 | |
| 25 | AddressSealed []byte `gorm:"column:address_sealed"` |
| 26 | |
| 27 | // AddressFP is a KEYED fingerprint (seal.Fingerprint), never a bare |
| 28 | // hash. It is what makes "is this person already on this event" |
| 29 | // answerable without a plaintext column — and it is keyed because |
| 30 | // the set of real addresses is small enough to enumerate, so an |
| 31 | // unkeyed SHA-256 of an address is that address in a different |
| 32 | // alphabet and would hand back everything the sealing just paid |
| 33 | // for. |
| 34 | AddressFP string `gorm:"column:address_fp;not null;index"` |
| 35 | |
| 36 | CreatedAt time.Time `gorm:"column:created_at;not null"` |
| 37 | } |
| 38 | |