logo logo
Design: Database: User meta

Database User
Purpose Hold a member's public profile and the topics they create.
Cardinality exactly one User database per member
one NextTopicItem in each User database
one VerifyItem in each User database
one EscrowUserItem in each User database for members who haven't accepted their invitation
one ProfileItem in each User database
one TopicItem item per topic made by the member
Source Code
Items src/ts/dbitems/User.ts
Zod Models
item UserItem - union of NextTopicItem, VerifyItem, EscrowUserItem, ProfileItem, TopicItem below
item.item one of NextTopicRecord, VerifyRecord, EscrowUserRecord, ProfileRecord, TopicRecord
NextTopicItem
itemId 'nexttopic'
item: NextTopicRecord
    kind 'nexttopic'
    mnum this member's member number
    nexttnum next topic number to assign (first is 1)
VerifyItem
itemId 'verify'
item: VerifyRecord
    kind 'verify'
    mnum this member's member number
    message Userbase verification message for user
EscrowUserItem
itemId 'escrowuser'
item: EscrowUserRecord
    kind 'escrowuser'
    mnum this member's member number
    message Userbase verification message for ESCROW user
    username Userbase username for ESCROW user
ProfileItem
itemId 'profile'
item: ProfileRecord
    kind 'profile'
    mnum this member's member number
    hasThumbnail true if item has associated thumbnail stored as a file
    initials member's initials
    title member's title
    subtitle optional member's subtitle
    paragraph optional member's paragraph
    moniker member's moniker
    accepted_on posix millis utc when invitation accepted (0 if not)
    home optional ProfileHomeTopic or ProfileHomeBundle indicating page member should see after joining
ProfileHomeTopic
kind 'home topic'
tkey topic member should see after joining
ProfileHomeBundle
kind 'home bundle'
bnum bundle member should see after joining
TopicItem
itemId topic key (creator ++ transliterated topic number)
item: TopicRecord
    kind 'topic'
    mnum this member's member number
    tnum this topic's topic number
    tid TID of the topic, used as prefix for TID-Topic database name
    dbid dbid of the corresponding TID-Topic database

Description

A User database is created when the host creates an engagement or invites a new member to the engagement.

It holds one NextTopicItem to track the number of the next topic created by the member.

It holds one VerifyItem for the member's Userbase verification message.

It holds one EscrowUserItem for an invited member's ESCROW user Userbase verification message and username.

It holds one ProfileItem for the member's profile.

It holds one TopicItem for each discussion topic created by the member.

(see userbase-js/types/index.d.ts)


  export interface Item {              userbase item
    itemId: string                    ╭─────────────────────┐
    item: any ────────────────────┬──▶│ kind: 'nexttopic'   │
    createdBy: Attribution        │   │ nexttnum: 1         │
    updatedBy?: Attribution       │   ╰─────────────────────┘
    fileId?: string               │
    fileName?: string             │   ╭────────────────────────────────────────┐
    fileSize?: number             ├──▶│ kind: 'verify'                         │
    fileUploadedBy?: Attribution  │   │ messgage: 'mFtZSI6IjeyJ1c2Vyb...'      │
    writeAccess?: AccessControl   │   ╰────────────────────────────────────────┘
  }                               │
                                  │   ╭────────────────────────────────────────┐
                                  ├──▶│ kind: 'escrowuser'                     │
                                  │   │ mnum: 2                                │
                                  │   │ message:  'eyJ1c2VybmFtZSI6Inl...'     │
                                  │   │ username: '01hss3d1pj3e0bvhy8khcqeys6' │
                                  │   ╰────────────────────────────────────────┘
                                  │
                                  │   ╭─────────────────────┐    file
                                  ├──▶│ kind: 'profile'     │   ┌────────────────┐
                                  │   │ mnum: 2             │╶╶ │ thumbnail data │
                                  │   │ hasThumbnail: true  │   └────────────────┘
                                  │   │ ...                 │
                                  │   ╰─────────────────────┘
                                  │
                                  │   ╭────────────────────────────────────────┐
                                  └──▶│ kind: 'topic'                          │
                                      │ mnum: 2                                │
                                      │ tnum: 1                                │
                                      │ tid:  'THFVJ01H6ETV4ZBRQFXMMRX23G'     │
                                      │ dbid: 'G32XRMMFHTXFQRBZ4VTE6H10JV'     │
                                      ╰────────────────────────────────────────┘

UserItem represents the specific type of Item.item:
  NextTopicItem, VerifyItem, EscrowUserItem, ProfileItem or TopicItem.

NextTopicItem

Each member's User database has a single NextTopicItem which holds a serial 'next topic' number which starts with 1 and is incremented each time the member creates a topic.

VerifyItem

Each member's User database has a single VerifyItem which holds the user's Userbase verification message which is set when the member is added and updated each time the member changes their userbase information.

EscrowUserItem

The User database of each member who has not accepted their invitation has a single EscrowUserItem which holds the member's escrow user Userbase verification message and username which is set when the member is added.

The EscrowUserItem is removed when a user accepts their invitation.

ProfileItem

Each member's User database has a ProfileItem with a few identifying facts shared with other members. The profile facts are initially set by the host when the member is invited. A member can change the facts after they've accepted the invitation.

TopicItem

Each member's User database has a TopicItem for each topic the member creates. The TopicItem identifies the TID-Topic database with the topic's details and members.

The itemId of the TopicItem, also known as the tkey, is the concatenation of the digits of the member number of the topic's creator and the digits of the topic number transliterated as follows

    0 1 2 3 4 5 6 7 8 9 
    │ │ │ │ │ │ │ │ │ │ 
    ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ 
    Z A B C D E F G H J 

For example "3B" indicates the second topic (B) created by member 3.

Ownership and Access

The member is the owner of their User database.

Other members in the engagement have read permission to the member's User database.

Zod models

The following Zod models show how the Userbase items are represented in memory.

export type UserItem =
  | NextTopicItem
  | VerifyItem
  | EscrowUserItem
  | ProfileItem
  | TopicItem
;
export const UserItem = z.discriminatedUnion('kind', [
  NextTopicItem,
  VerifyItem,
  EscrowUserItem,
  ProfileItem,
  TopicItem
]);

NextTopicItem

In addition to the values specified by the Userbase Item interface the kind and database attributes are added to NextTopicItem during deserialization.

export type  NextTopicItem = z.infer<typeof NextTopicItem>;
export const NextTopicItem = z.object({
  kind:                 z.literal('nexttopicitem'),
  itemId:               z.string(),
  item:                 NextTopicRecord,
  updatedBy:            z.custom<Attribution>(),
  database:             z.custom<Database>()
}).strip();
export type  NextTopicRecord = z.infer<typeof NextTopicRecord>;
export const NextTopicRecord = z.object({
  kind:                 z.literal('nexttopic'),
  mnum:                 z.number(),
  nexttnum:             z.number(),
}).strip();

VerifyItem

In addition to the values specified by the Userbase Item interface the kind and database attributes are added to VerifyItem during deserialization.

export type  VerifyItem = z.infer<typeof VerifyItem>;
export const VerifyItem = z.object({
  kind:                 z.literal('verifyitem'),
  itemId:               z.string(),
  item:                 VerifyRecord,
  updatedBy:            z.custom<Attribution>(),
  database:             z.custom<Database>()
}).strip();
export type  VerifyRecord = z.infer<typeof VerifyRecord>;
export const VerifyRecord = z.object({
  kind:                 z.literal('verify'),
  mnum:                 z.number(),
  message:              z.string()
}).strip();

EscrowUserItem

In addition to the values specified by the Userbase Item interface the kind and database attributes are added to EscrowUserItem during deserialization.

export type  EscrowUserItem = z.infer<typeof EscrowUserItem>;
export const EscrowUserItem = z.object({
  kind:                 z.literal('escrowuseritem'),
  itemId:               z.string(),
  item:                 EscrowUserRecord,
  updatedBy:            z.custom<Attribution>(),
  database:             z.custom<Database>()
}).strip();
export type  EscrowUserRecord = z.infer<typeof EscrowUserRecord>;
export const EscrowUserRecord = z.object({
  kind:                 z.literal('escrowuser'),
  mnum:                 z.number(),
  message:              z.string(),
  username:             z.string()
}).strip();

ProfileItem

In addition to the values specified by the Userbase Item interface the kind, database and thumbnail attributes are added to ProfileItem during deserialization.

export type  ProfileItem = z.infer<typeof ProfileItem>;
export const ProfileItem = z.object({
  kind:                 z.literal('profileitem'),
  fileId:               z.string().optional(),
  itemId:               z.string(),
  item:                 ProfileRecord,
  updatedBy:            z.custom<Attribution>(),
  database:             z.custom<Database>(),
  thumbnail:            z.string().optional()
}).strip();
export type  ProfileRecord = z.infer<typeof ProfileRecord>;
export const ProfileRecord = z.object({
  kind:                 z.literal('profile'),
  mnum:                 z.number(),
  hasThumbnail:         z.boolean(), //thumbnail stored as a file?
  initials:             z.string(),
  title:                z.string(),
  subtitle:             z.string().optional(),
  paragraph:            z.string().optional(),
  moniker:              z.string(),
  accepted_on:          z.number(),
  home:                 ProfileHomePage.optional()
}).strip();
export type ProfileHomePage =
  | ProfileHomeTopic
  | ProfileHomeBundle
;
export const ProfileHomePage = z.discriminatedUnion('kind', [
  ProfileHomeTopic,
  ProfileHomeBundle
]);
export type  ProfileHomeTopic = z.infer<typeof ProfileHomeTopic>;
export const ProfileHomeTopic = z.object({
  kind:                 z.literal('home topic'),
  tkey:                 z.string()
}).strip();
export type  ProfileHomeBundle = z.infer<typeof ProfileHomeBundle>;
export const ProfileHomeBundle = z.object({
  kind:                 z.literal('home bundle'),
  bnum:                 z.number()
}).strip();

TopicItem

In addition to the values specified by the Userbase Item interface the kind and database attributes are added to TopicItem during deserialization.

export type  TopicItem = z.infer<typeof TopicItem>;
export const TopicItem = z.object({
  kind:                 z.literal('topicitem'),
  itemId:               z.string(),
  item:                 TopicRecord,
  updatedBy:            z.custom<Attribution>(),
  database:             z.custom<Database>()
}).strip();
export type  TopicRecord = z.infer<typeof TopicRecord>;
export const TopicRecord = z.object({
  kind:                 z.literal('topic'),
  mnum:                 z.number(),
  tnum:                 z.number(),
  tid:                  z.string(),
  dbid:                 z.string()
}).strip();