ssb-profile

    3.2.0 • Public • Published

    ssb-profile

    An secret-stack plugin for creating, reading, updating profiles in scuttlebutt

    Example Usage

     const Stack = require('secret-stack')
     const caps = require('ssb-caps')
     const Config = require('ssb-config/inject')
     
     const config = Config({})
     
     const ssb = Stack({ caps })
       .use(require('ssb-db'))
    +  .use(require('ssb-backlinks')) // required
    +  .use(require('ssb-query')) //     required
    +  .use(require('ssb-profile'))
       .use(require('ssb-tribes')) //    (optonal) support for private messages
       .call(null, config)
     
     const details = {
       preferredName: 'Ben',
       avatarImage: {
         blob: '&CLbw5B9d5+H59oxDNOy4bOkwIaOhfLfqOLm1MGKyTLI=.sha256',
         mimeType: 'image/png'
       }
     }
     
     ssb.profile.publicPerson.create(details, (err, profileId) => {
       // ...
     })
    // later:
    ssb.profile.publicPerson.get(profileId, (err, profile) => {
      // ...
    })
    
    // or:
    const update = {
      preferredName: 'Ben Tairea',
    }
    
    ssb.profile.publicPerson.update(profileId, update, (err, updateMsg) => {
      // ...
    })

    Requirements

    An ssb-server with the following plugins:

    • ssb-backlinks
    • ssb-query

    API

    Person profile (PUBLIC)

    Handles public facing (unencrypted) profiles of type profile/person.

    ssb.profile.publicPerson

    • .create(details, cb)
    • .get(profileId, cb)
    • .update(profileId, details, cb)
    • .tombstone(profileId, details, cb)

    Here details is an Object which allows:

    {
      authors: {
        add: [Author]    // required on .create
        remove: [Author]
      },
    
      preferredName: String,
      gender: Gender,
      avatarImage: Image,
    
      tombstone: Tombstone
    
      // allowPublic: Booelan // if using ssb-recps-guard
    }

    NOTES:

    • authors is a special field which defines permissions for updates
      • you must set authors.add when creating a record
    • This type is deliberatly quite limited, to avoid accidental sharing of private data.
    • All fields (apart from authors) can also be set to null
    • See below for types.

    Person profile (PRIVATE)

    Handles encrypted profiles of type profile/person.

    ssb.profile.privatePerson

    • .create(details, cb)
    • .get(profileId, cb)
    • .update(profileId, details, cb)
    • .tombstone(profileId, details, cb)

    Here details is an Object:

    {
      recps: [Recp],     // required
      authors: {
        add: [Author]    // required on .create
        remove: [Author]
      },
    
      preferredName: String,
      legalName: String,
      altNames: {
        add: [String],
        remove: [String]
      },
    
      avatarImage: Image,
      headerImage: Image,
    
      description: String, 
      gender: Gender,
    
      aliveInterval: EdtfIntervalString,
      deceased: Boolean,
      placeOfBirth: String,
      placeOfDeath: String,
      buriedLocation: String,
      birthOrder: Int,
    
      profession: String,
      education: [String], // overwrites last Array of Strings
      school: [String],    // overwrites last Array of Strings
    
      address: String, 
      city: String,
      country: String,
      postCode: String,
      phone: String,
      email: String,
    
      tombstone: Tombstone
    }

    NOTES:

    • authors is a special field which defines permissions for updates
      • you must set authors.add when creating a record
    • recps is required when creating, but updates copy the initial recps
    • All fields (apart from authors, altNames) can also be set to null
    • See below for Types

    Community profile (PUBLIC)

    Handles public facing (unencrypted) profiles of type profile/community.

    ssb.profile.publicCommunity

    • .create(details, cb)
    • .get(profileId, cb)
    • .update(profileId, details, cb)
    • .tombstone(profileId, details, cb)

    Here details is an Object which allows:

    {
      authors: {
        add: [Author]    // required on .create
        remove: [Author],
      },
    
      preferredName: String,
      description: String,
    
      avatarImage: Image,
      headerImage: Image,
    
      address: String,
      city: String,
      country: String,
      postCode: String,
      phone: String,
      email: String,
    
      joiningQuestions: CustomForm, // only difference from private
    
      tombstone: Tombstone
    
      // allowPublic: Boolean       // if using ssb-recps-guard
    }

    NOTES:

    • authors is a special field which defines permissions for updates
      • you must set authors.add when creating a record
    • All fields (apart from authors) can also be set to null
    • See below for Types

    Community profile (PRIVATE)

    Handles encrypted profiles of type profile/community.

    ssb.profile.privateCommunity

    • .create(details, cb)
    • .get(profileId, cb)
    • .update(profileId, details, cb)
    • .tombstone(profileId, details, cb)

    Here details is an Object which allows:

    {
      recps: [Recp],     // required
      authors: {
        add: [Author]    // required on .create
        remove: [Author],
      },
    
      preferredName: String,
      description: String,
    
      avatarImage: Image,
      headerImage: Image,
    
      address: String,
      city: String,
      country: String,
      postCode: String,
      phone: String,
      email: String,
    
      tombstone: Tombstone
    }

    NOTES:

    • recps is required when creating, but updates copy the initial recps
    • authors is a special field which defines permissions for updates
      • you must set authors.add when creating a record
    • All fields (apart from authors) can also be set to null
    • See below for Types

    How get methods work

    Because there might be multiple offline edits to a profile which didn't know bout one-another, it's possible for divergence to happen:

       A   (the root message)
       |
       B   (an edit after A)
      / \
     C   D (two concurrent edits after B)
    

    profile is an Object which maps the key of a each latest edit to the state it perceives the profile to be in! So for that prior example:

    // profile
    {
      key: MessageId,         // the root message of the profile tangle, aka profileId
      type: ProfileType,
      recps: [Recp],          // recipients (will be null on public records)
      originalAuthor: FeedId
      states: [
        { key: C, ...state },
        { key: D, ...state },
      ]
    }

    where

    • recps is the private recipients who can access the profile
    • states [State]_ - the one / multiple states in which the profile is in:
      • these are sorted from most to least recent edit (by asserted publishedDate on the last update message)
      • key MessageId is the key of the message which is the most recent edit
      • state is an object which shows what the state of the profile is (from the perspective of a person standing at that particular "head")
      • e.g. for some Public Person profile, it might look like:
        // State
        {
          type: 'person'                      // added to help you out
        
          authors: {
            '@xIP5FV16FwPUiIZ0TmINhoCo4Hdx6c4KQcznEDeWtWg=.ed25519': [
              { start: 203, end: Integer }
            ]
          },
          preferredName: 'Ben Tairea',
          gender: 'male',                           
          tombstone: null                     // all profile fields are present, are "null" if unset
        }

    Fields which get reduced:

    • authors returns a collection of authors, and "intervals" for which that author was active
      • these are sequence numbers from the authors feed (unless the author is "*" in which case it's a time-stamp)
    • altNames returns an Array of names (ordered is not guarenteed)

    ssb.profile.link.create(profileId, opts, cb)

    where

    • profileId MessageId is the profile you're creating a link to
    • opts Object (optional) allows you to tune the link:
      • opts.feedId FeedId if provided creates a link/feed-profile with provided feedId instead of current ssb instance's feedId
      • opts.groupId GroupId creates a link/group-profile
      • opts.allowPublic Boolean (optional) - if you have ssb-recps-guard installed and want to bypass it for a public (unencrypted) link
    • cb Function - callback with signature (err, link) where link is the link message

    Note:

    • if you link to a private profile, the link will be encrypted to the same recps as that profile
    • if you provide opts.feedId and opts.groupId you will get an error

    Find methods

    ssb.profile.find(opts, cb)

    Arguments:

    • opts Object - an options object which currently expects properties:
      • type String - type of profile to find ("person"|"community")
      • name String - a name that could be part of a preferredName or legalName or altNames
      • includeTombstoned Boolean (optional) - whether to include profiles which habe been tombstoned (default: false)
    • cb Function - a callback with signature (err, suggestions) where suggestions is an array of Profiles

    ssb.profile.findByFeedId(feedId, cb)

    Takes a feedId and calls back with all profiles which that feedId has linked to it. Signature of cb is cb(err, profiles) where profiles is of form:

    {
      public: [Profile],
      private: [Profile]
    }

    NOTE:

    • profiles which have been tombstoned are not included in results
    • profiles are ordered from oldest to newest in terms of when they were linked to the feedId
    • advanced : ssb.profile.findByFeedId(feedId, opts, cb)
      • opts.getProfile - provide your own getter. signature getProfile(profileId, cb)
        • callback with cb(null, null) if you want to exclude a result
        • useful if you want to add a cache to your getter, or only allow certain types of profile
      • opts.groupId GroupId - only return profiles that exist in a particular private group
      • opts.sortPublicPrivate Booolean - whether to sort into { public, private }
        • default: true
        • if false returns an Array of profiles
      • opts.selfLinkOnly Boolean - only include profiles where the link message was authored by the feedId
        • default: true
        • if false, public and private groupings are further split into self and other:
          {
            self: { public: [Profile], private: [Profile] },
            other: { public: [Profile], private: [Profile] }
          }
        • if false you get profiles that anyone has linked to that feedId,
          • WARNING links asserted by others could be malicious
          • if you trust your context this can be a useful fallback

    ssb.profile.findByGroupId(groupId, cb)

    Takes a groupId and calls back with all profiles which that feedId has linked to it. Signature of cb is cb(err, profiles) where profiles is of form:

    {
      public: [Profile],
      private: [Profile]
    }

    NOTE:

    • profiles which have been tombstoned are not included in results
    • profiles are ordered from oldest to newest in terms of when they were linked to the feedId
    • you can call this with ssb.profile.findByGroupId(feedId, { getProfile }, cb), useful if you have a getter with a cache

    ssb.profile.findFeedsByProfile(profileId, cb)

    Takes a profileId and calls back with all the feedIds which that profileId has linked to it. Signature of cb is cb(err, feeds) where feeds is of form:

    [FeedId, FeedId, ...]

    Types

    • Author String a FeedId or "*" (i.e. any user)
      • any updates that arent from a valid author are classed as invalid and will be ignored when using the get method
    • Recp String a "recipient", usually a FeedId or GroupId
      • the record will be encrypted so only that recipient(s) can access the record
      • requires an encryption plugin to be installed e.g. ssb-tribes, ssb-private1
    • Image Object:
      {
        blob: Blob,        // the content address for the blob (with prefex &)
        mimeType: String,  // mimetype of the image
        unbox: UnboxKey,   // (optional) a String for unboxing the blob if encrypted
        size: Number,      // (optional) size of the image in bytes
        width: Number,     // (optional) width of image in pixels
        height: Number     // (optional) height of image in pixels
      }
    • Gender String (male|female|other|unknown)
    • EdtfIntervalString - see edtf module and library of congress spec
    • Tombstone Object
      {
        date: UnixTime,  // an Integer indicating microseconds from 1st Jan 1970, can be negative!
        reason: String   // (optional)
      }
    • UnixTime Integer microseconds since 00:00 1st Jan 1970 (can be negative, read more)
    • CustomForm [FormField] - used generate custom form for people applying to join a community. e.g
      [
        { type: 'input', label: 'Who introduced you?' },
        { type: 'textarea', label: 'Please tell use about yourself' },
      ]
      • FormField Object of shape:
        {
          type: FieldType, // String: input|textarea
          label: String
        }

    FAQ

    I want to delete my legalName, how do?

    • first, know that if you previously published a legalName it will always be part of your record (even if it's not currently displayed)
    • if you want to clear a text field, just publish an update with null value: { legalName: null }

    How do I clear an image?

    • same as with legalName - set it to null

    Multiple editors for a profile?

    • work in progress!
    • currently supports multiple writers, but does not support merging of branched state
      • by default, .update extends the most recent branch

    Development

    Project layout (made with tree):

    .
    ├── index.js           // ssb-server plugin (collects all methods)
    ├── method             // user facing methods
    ├── spec               // describes message + how to reduce them
    │   ├── person
    │   │   ├── public
    │   │   └── private
    │   ├── community
    │   │   ├── public
    │   │   └── private
    │   ├── link
    │   │   ├── feed-profile
    │   │   └── group-profile
    │   └── lib
    │
    └── test               // tests!
    

    run npm test to run tests

    Install

    npm i ssb-profile

    DownloadsWeekly Downloads

    16

    Version

    3.2.0

    License

    AGPL-3.0

    Unpacked Size

    133 kB

    Total Files

    42

    Last publish

    Collaborators

    • avatar
    • avatar
    • avatar
    • avatar
    • avatar
    • avatar
    • avatar
    • avatar
    • avatar
    • avatar
    • avatar
    • avatar
    • avatar
    • avatar
    • avatar
    • avatar
    • avatar
    • avatar
    • avatar
    • avatar