{
  "rules": {

    "users": {
      // make sure top parent rules are not open to everyone
      // children rules can grant extra access, but cant revoke already granted parent accees
      // only grant access to children nodes based on need
      "$user_email": {
        ".read": "auth !== null && auth.uid !== null && auth.email_verified == true && auth.email.replace('.','_') === $user_email",
        ".write": "auth !== null && auth.uid !== null && auth.email_verified == true && auth.email.replace('.','_') === $user_email",
        
        // public child node path is read access for everyone
        // but write access is granular
        "public": {
          ".read": "auth !== null && auth.uid !== null && auth.email_verified == true",

          // other users can write only to this public node pending-text child
          // it is used to receive text peanuts from other users
          // once received, it is deleted if the user accepts it and copies it to their stash
          "pending-text": {
            // give owner read write access
            ".read":  "auth !== null && auth.uid !== null && auth.email_verified == true && root.child('users/' + $user_email + '/private/uid').val() === auth.uid",
            ".write": "auth !== null && auth.uid !== null && auth.email_verified == true && root.child('users/' + $user_email + '/private/uid').val() === auth.uid",
            // validate schema and enforce that others can only use their own uid and email to write
            // and make sure that userId of this contact is already added as a prop in private/contacts
            "$text":{
              // only owner can read this node
              ".read" : false,
              // whitelisted colleagues can write 
              ".write" : "root.child('/users/' + $user_email + '/private/contacts/' + auth.email.replace('.','_') ).exists()",
              "email": { ".validate": "newData.isString() && newData.val() === auth.email && newData.val().length <= 128"},
              "userId": { ".validate": "newData.isString() && newData.val() === auth.uid && newData.val().length <= 32"}, 
              "timestamp": { ".validate": "newData.isNumber()"},
              "data": { ".validate": "newData.isString() && newData.val().length <= 1024"},
              "note": { ".validate": "newData.isString() && newData.val().length <= 512"},
               "$other": { ".validate": false }
            }
          },

          // uid, publickey and email are write access only for the current user
          "uid": {
            ".read": "auth !== null && auth.uid !== null && auth.email_verified == true",
            ".write": "auth !== null && auth.uid !== null  && auth.email_verified == true && root.child('users/' + $user_email + '/public/uid').val() === auth.uid",
            ".validate": "newData.isString() && newData.val().length <= 32" 
          },

          "publicKey": {
            ".read": "auth !== null && auth.uid !== null && auth.email_verified == true",
            ".write": "auth !== null && auth.uid !== null && auth.email_verified == true && root.child('users/' + $user_email + '/public/uid').val() === auth.uid",
            ".validate": "newData.isString() && newData.val().length <= 512"  
          },

          "email": {
            ".read": "auth !== null && auth.uid !== null && auth.email_verified == true",
            ".write": "auth !== null && auth.uid !== null && auth.email_verified == true && root.child('users/' + $user_email + '/public/uid').val() === auth.uid",
            ".validate": "newData.isString() && newData.val().length <= 128" 
          },
          "$other": { ".validate": false }
        },

        // private child node and its children is only read/write access for the current user
        "private": {
          ".read": "auth !== null && auth.uid !== null && auth.email_verified == true && root.child('users/' + $user_email + '/private/uid').val() === auth.uid",
          ".write": "auth !== null && auth.uid !== null && auth.email_verified == true && root.child('users/' + $user_email + '/private/uid').val() === auth.uid",
          "peanut-stash" : {
            "$text":{
              ".indexOn": "timestamp",  // index on timestamp for query performance
              "userEmail": { ".validate": "newData.isString() && newData.val().length <= 128"},
              "userId": { ".validate": "newData.isString() && newData.val().length <= 32"}, 
              "timestamp": { ".validate": "newData.isNumber()"},
              "data": { ".validate": "newData.isString() && newData.val().length <= 1024"},
              "note": { ".validate": "newData.isString() && newData.val().length <= 512"},
              "category": { ".validate": "newData.isString() && newData.val().length <= 32"},
              "$other": { ".validate": false }
            }
          },
          "peanut-alias" : {
            ".indexOn": "name",  // index on name for query performance
            "$text":{
              "timestamp": { ".validate": "newData.isNumber()"},
              "name": { ".validate": "newData.isString() && newData.val().length <= 32"}, 
              "parent": { ".validate": "newData.isString() && newData.val().length <= 20"}, // firebase key size
              "$other": { ".validate": false }
            }
          },
          "categories": {
            "$category":{
              "name": { ".validate": "newData.isString() && newData.val().length <= 32"},
              "$other": { ".validate": false }
            }
          },
          "contacts":{
            "$contact": { ".validate": "newData.isString() && newData.val().length <= 128" }
          },
          "uid" : {".validate": "newData.isString() && newData.val().length <= 32"},
          "privateKey" : {".validate": "newData.isString() && newData.val().length <= 2048"},
          "$other": { ".validate": false }
        },
        "$other": { ".validate": false }
      }
    },
    "$other": { ".validate": false }
  }
}