Ecoer Logo
sanderpick

@sanderpick

25

Co-founder/CTO @ https://textile.io . Having fun with p2p networks, cryptography, and rock climbing. Lover of free speech and the open marketplace of ideas.

hive.blog/@sanderpick
VOTING POWER100.00%
DOWNVOTE POWER100.00%
RESOURCE CREDITS100.00%
REPUTATION PROGRESS0.00%
Net Worth
11.817USD
HIVE
0.000HIVE
HBD
0.000HBD
Own HP
29.478HP

Detailed Balance

HIVE
balance
0.000HIVE
market_balance
0.000HIVE
savings_balance
0.000HIVE
reward_hive_balance
0.000HIVE
HIVE POWER
Own HP
29.478HP
Delegated Out
0.000HP
Delegation In
0.000HP
Effective Power
29.478HP
Reward HP (pending)
0.034HP
HBD
hbd_balance
0.000HBD
hbd_conversions
0.000HBD
hbd_market_balance
0.000HBD
savings_hbd_balance
0.000HBD
reward_hbd_balance
0.026HBD
{
  "balance": "0.000 HIVE",
  "savings_balance": "0.000 HIVE",
  "reward_hive_balance": "0.000 HIVE",
  "vesting_shares": "47853.206961 VESTS",
  "delegated_vesting_shares": "0.000000 VESTS",
  "received_vesting_shares": "0.000000 VESTS",
  "hbd_balance": "0.000 HBD",
  "savings_hbd_balance": "0.000 HBD",
  "reward_hbd_balance": "0.026 HBD"
}

Account Info

namesanderpick
id791306
rank0
reputation0
created2018-03-02T17:19:30
recovery_accountsteem
proxyNone
invited_bynull
post_count3
comment_count0
lifetime_vote_count0
witnesses_voted_for0
last_post2018-11-01T21:52:03
last_root_post2018-11-01T21:33:18
last_vote_time2018-11-01T23:32:57
proxied_vsf_votes0, 0, 0, 0
can_vote1
voting_power9,522
delayed_votesNone
governance_vote_expiration_ts1969-12-31T23:59:59
balance0.000 HIVE
savings_balance0.000 HIVE
hbd_balance0.000 HBD
savings_hbd_balance0.000 HBD
vesting_shares47853.206961 VESTS
delegated_vesting_shares0.000000 VESTS
received_vesting_shares0.000000 VESTS
reward_vesting_balance68.543020 VESTS
vesting_balance0.000 HIVE
vesting_withdraw_rate0.000000 VESTS
next_vesting_withdrawal1969-12-31T23:59:59
withdrawn0
to_withdraw0
withdraw_routes0
savings_withdraw_requests0
last_account_recovery1970-01-01T00:00:00
reset_accountnull
last_owner_update1970-01-01T00:00:00
last_account_update2018-11-01T15:23:54
minedNo
hbd_seconds0
hbd_last_interest_payment1970-01-01T00:00:00
savings_hbd_last_interest_payment1970-01-01T00:00:00
{
  "id": 791306,
  "name": "sanderpick",
  "owner": {
    "weight_threshold": 1,
    "account_auths": [],
    "key_auths": [
      [
        "STM5miWoPgXh9Coxgg462uDpuAuBuVa5jmGEEqCxFLCzxuWovxvhy",
        1
      ]
    ]
  },
  "active": {
    "weight_threshold": 1,
    "account_auths": [],
    "key_auths": [
      [
        "STM87JJzyvJ9e8Rf6vm72fDaQtET6dKWPqHycSmfbvKziP4QpGHQw",
        1
      ]
    ]
  },
  "posting": {
    "weight_threshold": 1,
    "account_auths": [],
    "key_auths": [
      [
        "STM6BTaccrcMfAkSe4csndTguQMXXD6EXtRXE6wSeFW5p67EcZi3k",
        1
      ]
    ]
  },
  "memo_key": "STM5CZugansgXw2GtjWmHu9kiiPrpvpiGHNGGw9d4yeb97zVFsdw3",
  "json_metadata": "{\"profile\":{\"profile_image\":\"https://cdn.steemitimages.com/DQmcvgiUj7JsQ9yqVgDuehbAwiB7b5sokTH9YUvaMVzbBTz/IMG_8874.jpg\",\"cover_image\":\"https://ipfs.textile.io/ipfs/QmbmwFiGQG4ZbzcervBRYiVMGqi56oWcSTcx2Do8YaAGZ5\",\"name\":\"Sander Pick\",\"about\":\"Co-founder/CTO @ https://textile.io . Having fun with p2p networks, cryptography, and rock climbing. Lover of free speech and the open marketplace of ideas.\",\"location\":\"Sunnyvale, CA\",\"website\":\"https://www.textile.io\"}}",
  "posting_json_metadata": "{\"profile\":{\"profile_image\":\"https://cdn.steemitimages.com/DQmcvgiUj7JsQ9yqVgDuehbAwiB7b5sokTH9YUvaMVzbBTz/IMG_8874.jpg\",\"cover_image\":\"https://ipfs.textile.io/ipfs/QmbmwFiGQG4ZbzcervBRYiVMGqi56oWcSTcx2Do8YaAGZ5\",\"name\":\"Sander Pick\",\"about\":\"Co-founder/CTO @ https://textile.io . Having fun with p2p networks, cryptography, and rock climbing. Lover of free speech and the open marketplace of ideas.\",\"location\":\"Sunnyvale, CA\",\"website\":\"https://www.textile.io\"}}",
  "proxy": "",
  "previous_owner_update": "1970-01-01T00:00:00",
  "last_owner_update": "1970-01-01T00:00:00",
  "last_account_update": "2018-11-01T15:23:54",
  "created": "2018-03-02T17:19:30",
  "mined": false,
  "recovery_account": "steem",
  "last_account_recovery": "1970-01-01T00:00:00",
  "reset_account": "null",
  "comment_count": 0,
  "lifetime_vote_count": 0,
  "post_count": 3,
  "can_vote": true,
  "voting_manabar": {
    "current_mana": 45566893283,
    "last_update_time": 1541115177
  },
  "downvote_manabar": {
    "current_mana": 0,
    "last_update_time": 1520011167
  },
  "voting_power": 9522,
  "balance": "0.000 HIVE",
  "savings_balance": "0.000 HIVE",
  "hbd_balance": "0.000 HBD",
  "hbd_seconds": "0",
  "hbd_seconds_last_update": "1970-01-01T00:00:00",
  "hbd_last_interest_payment": "1970-01-01T00:00:00",
  "savings_hbd_balance": "0.000 HBD",
  "savings_hbd_seconds": "0",
  "savings_hbd_seconds_last_update": "1970-01-01T00:00:00",
  "savings_hbd_last_interest_payment": "1970-01-01T00:00:00",
  "savings_withdraw_requests": 0,
  "reward_hbd_balance": "0.026 HBD",
  "reward_hive_balance": "0.000 HIVE",
  "reward_vesting_balance": "68.543020 VESTS",
  "reward_vesting_hive": "0.034 HIVE",
  "vesting_shares": "47853.206961 VESTS",
  "delegated_vesting_shares": "0.000000 VESTS",
  "received_vesting_shares": "0.000000 VESTS",
  "vesting_withdraw_rate": "0.000000 VESTS",
  "post_voting_power": "47853.206961 VESTS",
  "next_vesting_withdrawal": "1969-12-31T23:59:59",
  "withdrawn": 0,
  "to_withdraw": 0,
  "withdraw_routes": 0,
  "pending_transfers": 0,
  "curation_rewards": 0,
  "posting_rewards": 67,
  "proxied_vsf_votes": [
    0,
    0,
    0,
    0
  ],
  "witnesses_voted_for": 0,
  "last_post": "2018-11-01T21:52:03",
  "last_root_post": "2018-11-01T21:33:18",
  "last_vote_time": "2018-11-01T23:32:57",
  "post_bandwidth": 0,
  "pending_claimed_accounts": 0,
  "governance_vote_expiration_ts": "1969-12-31T23:59:59",
  "delayed_votes": [],
  "open_recurrent_transfers": 0,
  "vesting_balance": "0.000 HIVE",
  "reputation": 0,
  "transfer_history": [],
  "market_history": [],
  "post_history": [],
  "vote_history": [],
  "other_history": [],
  "witness_votes": [],
  "tags_usage": [],
  "guest_bloggers": [],
  "rank": 0
}

Withdraw Routes

IncomingOutgoing
Empty
Empty
{
  "incoming": [],
  "outgoing": []
}
From Date
To Date
2020/03/05 11:54:24
bodyCongratulations @sanderpick! You received a personal award! <table><tr><td>https://steemitimages.com/70x70/http://steemitboard.com/@sanderpick/birthday2.png</td><td>Happy Birthday! - You are on the Steem blockchain for 2 years!</td></tr></table> <sub>_You can view [your badges on your Steem Board](https://steemitboard.com/@sanderpick) and compare to others on the [Steem Ranking](https://steemitboard.com/ranking/index.php?name=sanderpick)_</sub> **Do not miss the last post from @steemitboard:** <table><tr><td><a href="https://steemit.com/steemitboard/@steemitboard/use-your-witness-votes-and-get-the-community-badge"><img src="https://steemitimages.com/64x128/https://cdn.steemitimages.com/DQmTugCUsoXX762vg1CuHRrpnPbfnjPogp8iCGv7F2kSVuj/image.png"></a></td><td><a href="https://steemit.com/steemitboard/@steemitboard/use-your-witness-votes-and-get-the-community-badge">Use your witness votes and get the Community Badge</a></td></tr></table> ###### [Vote for @Steemitboard as a witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1) to get one more award and increased upvotes!
title
authorsteemitboard
permlinksteemitboard-notify-sanderpick-20200305t115421000z
json metadata{"image":["https://steemitboard.com/img/notify.png"]}
parent authorsanderpick
parent permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
Transaction InfoBlock #41385112/Trx 5e351501d174372252672e0ca5e24bbc265d66fa
View Raw JSON Data
{
  "op": [
    "comment",
    {
      "body": "Congratulations @sanderpick! You received a personal award!\n\n<table><tr><td>https://steemitimages.com/70x70/http://steemitboard.com/@sanderpick/birthday2.png</td><td>Happy Birthday! - You are on the Steem blockchain for 2 years!</td></tr></table>\n\n<sub>_You can view [your badges on your Steem Board](https://steemitboard.com/@sanderpick) and compare to others on the [Steem Ranking](https://steemitboard.com/ranking/index.php?name=sanderpick)_</sub>\n\n\n**Do not miss the last post from @steemitboard:**\n<table><tr><td><a href=\"https://steemit.com/steemitboard/@steemitboard/use-your-witness-votes-and-get-the-community-badge\"><img src=\"https://steemitimages.com/64x128/https://cdn.steemitimages.com/DQmTugCUsoXX762vg1CuHRrpnPbfnjPogp8iCGv7F2kSVuj/image.png\"></a></td><td><a href=\"https://steemit.com/steemitboard/@steemitboard/use-your-witness-votes-and-get-the-community-badge\">Use your witness votes and get the Community Badge</a></td></tr></table>\n\n###### [Vote for @Steemitboard as a witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1) to get one more award and increased upvotes!",
      "title": "",
      "author": "steemitboard",
      "permlink": "steemitboard-notify-sanderpick-20200305t115421000z",
      "json_metadata": "{\"image\":[\"https://steemitboard.com/img/notify.png\"]}",
      "parent_author": "sanderpick",
      "parent_permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol"
    }
  ],
  "block": 41385112,
  "trx_id": "5e351501d174372252672e0ca5e24bbc265d66fa",
  "op_in_trx": 0,
  "timestamp": "2020-03-05T11:54:24",
  "virtual_op": false,
  "trx_in_block": 4
}
2019/03/02 20:06:15
bodyCongratulations @sanderpick! You received a personal award! <table><tr><td>https://steemitimages.com/70x70/http://steemitboard.com/@sanderpick/birthday1.png</td><td>Happy Birthday! - You are on the Steem blockchain for 1 year!</td></tr></table> <sub>_[Click here to view your Board](https://steemitboard.com/@sanderpick)_</sub> **Do not miss the last post from @steemitboard:** <table><tr><td><a href="https://steemit.com/carnival/@steemitboard/carnival-2019"><img src="https://steemitimages.com/64x128/http://i.cubeupload.com/rltzHT.png"></a></td><td><a href="https://steemit.com/carnival/@steemitboard/carnival-2019">Carnival Challenge - Collect badge and win 5 STEEM</a></td></tr></table> ###### [Vote for @Steemitboard as a witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1) and get one more award and increased upvotes!
title
authorsteemitboard
permlinksteemitboard-notify-sanderpick-20190302t200611000z
json metadata{"image":["https://steemitboard.com/img/notify.png"]}
parent authorsanderpick
parent permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
Transaction InfoBlock #30809751/Trx 5349ab4064f71f1564f9ce5db3d3778182b91f1f
View Raw JSON Data
{
  "op": [
    "comment",
    {
      "body": "Congratulations @sanderpick! You received a personal award!\n\n<table><tr><td>https://steemitimages.com/70x70/http://steemitboard.com/@sanderpick/birthday1.png</td><td>Happy Birthday! - You are on the Steem blockchain for 1 year!</td></tr></table>\n\n<sub>_[Click here to view your Board](https://steemitboard.com/@sanderpick)_</sub>\n\n\n**Do not miss the last post from @steemitboard:**\n<table><tr><td><a href=\"https://steemit.com/carnival/@steemitboard/carnival-2019\"><img src=\"https://steemitimages.com/64x128/http://i.cubeupload.com/rltzHT.png\"></a></td><td><a href=\"https://steemit.com/carnival/@steemitboard/carnival-2019\">Carnival Challenge - Collect badge and win 5 STEEM</a></td></tr></table>\n\n###### [Vote for @Steemitboard as a witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1) and get one more award and increased upvotes!",
      "title": "",
      "author": "steemitboard",
      "permlink": "steemitboard-notify-sanderpick-20190302t200611000z",
      "json_metadata": "{\"image\":[\"https://steemitboard.com/img/notify.png\"]}",
      "parent_author": "sanderpick",
      "parent_permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol"
    }
  ],
  "block": 30809751,
  "trx_id": "5349ab4064f71f1564f9ce5db3d3778182b91f1f",
  "op_in_trx": 0,
  "timestamp": "2019-03-02T20:06:15",
  "virtual_op": false,
  "trx_in_block": 3
}
steemdelegated 0.000 HP to @sanderpick
2018/11/26 19:21:15
delegateesanderpick
delegatorsteem
vesting shares0.000000 VESTS
Transaction InfoBlock #28046165/Trx 57d3bdcb6f92bfbd0f2d5e5e178126793abbf156
View Raw JSON Data
{
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "sanderpick",
      "delegator": "steem",
      "vesting_shares": "0.000000 VESTS"
    }
  ],
  "block": 28046165,
  "trx_id": "57d3bdcb6f92bfbd0f2d5e5e178126793abbf156",
  "op_in_trx": 1,
  "timestamp": "2018-11-26T19:21:15",
  "virtual_op": false,
  "trx_in_block": 11
}
2018/11/08 21:52:03
authorsanderpick
permlinkre-sanderpick-hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol-20181101t215203511z
Transaction InfoBlock #27531080/Virtual Operation 4294967295:6
View Raw JSON Data
{
  "op": [
    "comment_payout_update",
    {
      "author": "sanderpick",
      "permlink": "re-sanderpick-hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol-20181101t215203511z"
    }
  ],
  "block": 27531080,
  "trx_id": "0000000000000000000000000000000000000000",
  "op_in_trx": 6,
  "timestamp": "2018-11-08T21:52:03",
  "virtual_op": true,
  "trx_in_block": 4294967295
}
2018/11/08 21:33:18
authorsanderpick
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
Transaction InfoBlock #27530705/Virtual Operation 4294967295:6
View Raw JSON Data
{
  "op": [
    "comment_payout_update",
    {
      "author": "sanderpick",
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol"
    }
  ],
  "block": 27530705,
  "trx_id": "0000000000000000000000000000000000000000",
  "op_in_trx": 6,
  "timestamp": "2018-11-08T21:33:18",
  "virtual_op": true,
  "trx_in_block": 4294967295
}
2018/11/08 21:33:18
authorsanderpick
payout0.059 HBD
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
author rewards67
total payout value0.052 HBD
curator payout value0.006 HBD
beneficiary payout value0.000 HBD
Transaction InfoBlock #27530705/Virtual Operation 4294967295:5
View Raw JSON Data
{
  "op": [
    "comment_reward",
    {
      "author": "sanderpick",
      "payout": "0.059 HBD",
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol",
      "author_rewards": 67,
      "total_payout_value": "0.052 HBD",
      "curator_payout_value": "0.006 HBD",
      "beneficiary_payout_value": "0.000 HBD"
    }
  ],
  "block": 27530705,
  "trx_id": "0000000000000000000000000000000000000000",
  "op_in_trx": 5,
  "timestamp": "2018-11-08T21:33:18",
  "virtual_op": true,
  "trx_in_block": 4294967295
}
2018/11/08 21:33:18
authorsanderpick
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
hbd payout0.026 HBD
hive payout0.000 HIVE
vesting payout68.543020 VESTS
payout must be claimedtrue
curators vesting payout16.127769 VESTS
Transaction InfoBlock #27530705/Virtual Operation 4294967295:4
View Raw JSON Data
{
  "op": [
    "author_reward",
    {
      "author": "sanderpick",
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol",
      "hbd_payout": "0.026 HBD",
      "hive_payout": "0.000 HIVE",
      "vesting_payout": "68.543020 VESTS",
      "payout_must_be_claimed": true,
      "curators_vesting_payout": "16.127769 VESTS"
    }
  ],
  "block": 27530705,
  "trx_id": "0000000000000000000000000000000000000000",
  "op_in_trx": 4,
  "timestamp": "2018-11-08T21:33:18",
  "virtual_op": true,
  "trx_in_block": 4294967295
}
2018/11/08 16:30:27
authorsanderpick
permlinktextile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol
Transaction InfoBlock #27524649/Virtual Operation 4294967295:4
View Raw JSON Data
{
  "op": [
    "comment_payout_update",
    {
      "author": "sanderpick",
      "permlink": "textile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol"
    }
  ],
  "block": 27524649,
  "trx_id": "0000000000000000000000000000000000000000",
  "op_in_trx": 4,
  "timestamp": "2018-11-08T16:30:27",
  "virtual_op": true,
  "trx_in_block": 4294967295
}
2018/11/05 10:58:45
voterjohnnyyy
authorsanderpick
weight1064 (10.64%)
rshares558180116
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
pending payout0.072 HBD
total vote weight249140
Transaction InfoBlock #27431695/Trx e31b13a506828d497ee2ee0d90dbf4fbe9340962
View Raw JSON Data
{
  "op": [
    "effective_comment_vote",
    {
      "voter": "johnnyyy",
      "author": "sanderpick",
      "weight": 1064,
      "rshares": 558180116,
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol",
      "pending_payout": "0.072 HBD",
      "total_vote_weight": 249140
    }
  ],
  "block": 27431695,
  "trx_id": "e31b13a506828d497ee2ee0d90dbf4fbe9340962",
  "op_in_trx": 1,
  "timestamp": "2018-11-05T10:58:45",
  "virtual_op": true,
  "trx_in_block": 3
}
2018/11/05 10:58:45
voterjohnnyyy
authorsanderpick
weight10000 (100.00%)
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
Transaction InfoBlock #27431695/Trx e31b13a506828d497ee2ee0d90dbf4fbe9340962
View Raw JSON Data
{
  "op": [
    "vote",
    {
      "voter": "johnnyyy",
      "author": "sanderpick",
      "weight": 10000,
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol"
    }
  ],
  "block": 27431695,
  "trx_id": "e31b13a506828d497ee2ee0d90dbf4fbe9340962",
  "op_in_trx": 0,
  "timestamp": "2018-11-05T10:58:45",
  "virtual_op": false,
  "trx_in_block": 3
}
2018/11/02 02:56:06
bodyHi, Welcome to the amazing world of Blogging and Sharing on Steem Blockchain. Make sure to post only Original content and avoid all kind of plagarism. Keep your patience and faith to get success here. Also make sure to engage and contribite to Community development. Commenting is best part to get involve. Hope you have a happy stay.....Steem On! Thanks [@steemflow](https://steemit.com/@steemflow) Posted using [Partiko Android](https://steemit.com/@partiko-android)
title
authorsteemflow
permlinksteemflow-re-sanderpick-hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol-20181102t025603128z
json metadata{"app":"partiko"}
parent authorsanderpick
parent permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
Transaction InfoBlock #27335729/Trx b6df9faaf2bfdff5ef8dd8c6957845937d343d76
View Raw JSON Data
{
  "op": [
    "comment",
    {
      "body": "Hi, Welcome to the amazing world of Blogging and Sharing on Steem Blockchain. Make sure to post only Original content and avoid all kind of plagarism.\nKeep your patience and faith to get success here. Also make sure to engage and contribite to Community development. Commenting is best part to get involve. Hope you have a happy stay.....Steem On!\nThanks\n[@steemflow](https://steemit.com/@steemflow)\n\nPosted using [Partiko Android](https://steemit.com/@partiko-android)",
      "title": "",
      "author": "steemflow",
      "permlink": "steemflow-re-sanderpick-hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol-20181102t025603128z",
      "json_metadata": "{\"app\":\"partiko\"}",
      "parent_author": "sanderpick",
      "parent_permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol"
    }
  ],
  "block": 27335729,
  "trx_id": "b6df9faaf2bfdff5ef8dd8c6957845937d343d76",
  "op_in_trx": 0,
  "timestamp": "2018-11-02T02:56:06",
  "virtual_op": false,
  "trx_in_block": 22
}
2018/11/02 01:17:21
voterjalayn
authorsanderpick
weight29865
rshares15657544265
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
pending payout0.071 HBD
total vote weight248076
Transaction InfoBlock #27333754/Trx a27dcbb4182548a106f20759e539abe9c2df9070
View Raw JSON Data
{
  "op": [
    "effective_comment_vote",
    {
      "voter": "jalayn",
      "author": "sanderpick",
      "weight": 29865,
      "rshares": 15657544265,
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol",
      "pending_payout": "0.071 HBD",
      "total_vote_weight": 248076
    }
  ],
  "block": 27333754,
  "trx_id": "a27dcbb4182548a106f20759e539abe9c2df9070",
  "op_in_trx": 1,
  "timestamp": "2018-11-02T01:17:21",
  "virtual_op": true,
  "trx_in_block": 11
}
2018/11/02 01:17:21
voterjalayn
authorsanderpick
weight7000 (70.00%)
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
Transaction InfoBlock #27333754/Trx a27dcbb4182548a106f20759e539abe9c2df9070
View Raw JSON Data
{
  "op": [
    "vote",
    {
      "voter": "jalayn",
      "author": "sanderpick",
      "weight": 7000,
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol"
    }
  ],
  "block": 27333754,
  "trx_id": "a27dcbb4182548a106f20759e539abe9c2df9070",
  "op_in_trx": 0,
  "timestamp": "2018-11-02T01:17:21",
  "virtual_op": false,
  "trx_in_block": 11
}
2018/11/02 00:50:57
bodyCongratulations @sanderpick! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) : <table><tr><td>https://steemitimages.com/60x60/http://steemitboard.com/notifications/firstpost.png</td><td>You published your First Post</td></tr> <tr><td>https://steemitimages.com/60x60/http://steemitboard.com/notifications/firstvoted.png</td><td>You got a First Vote</td></tr> <tr><td>https://steemitimages.com/60x60/http://steemitboard.com/notifications/firstcomment.png</td><td>You made your First Comment</td></tr> <tr><td>https://steemitimages.com/60x70/http://steemitboard.com/@sanderpick/voted.png?201811012312</td><td>You received more than 10 upvotes. Your next target is to reach 50 upvotes.</td></tr> <tr><td>https://steemitimages.com/60x60/http://steemitboard.com/notifications/firstvote.png</td><td>You made your First Vote</td></tr> </table> <sub>_[Click here to view your Board of Honor](https://steemitboard.com/@sanderpick)_</sub> <sub>_If you no longer want to receive notifications, reply to this comment with the word_ `STOP`</sub> **Do not miss the last post from @steemitboard:** <table><tr><td><a href="https://steemit.com/steemfest/@steemitboard/i06trehc"><img src="https://steemitimages.com/64x128/https://ipfs.io/ipfs/QmU34ZrY632FFKQ1vbrkSM27VcnsjQdtXPynfMrpxDFJcF"></a></td><td><a href="https://steemit.com/steemfest/@steemitboard/i06trehc">Be ready for the next contest!</a></td></tr><tr><td><a href="https://steemit.com/halloween/@steemitboard/trick-or-treat-publish-your-scariest-halloweeen-story-and-win-a-new-badge"><img src="https://steemitimages.com/64x128/http://i.cubeupload.com/RUyB3u.png"></a></td><td><a href="https://steemit.com/halloween/@steemitboard/trick-or-treat-publish-your-scariest-halloweeen-story-and-win-a-new-badge">Trick or Treat - Publish your scariest halloween story and win a new badge</a></td></tr></table> > Support [SteemitBoard's project](https://steemit.com/@steemitboard)! **[Vote for its witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1)** and **get one more award**!
title
authorsteemitboard
permlinksteemitboard-notify-sanderpick-20181102t005053000z
json metadata{"image":["https://steemitboard.com/img/notify.png"]}
parent authorsanderpick
parent permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
Transaction InfoBlock #27333226/Trx 143d4e15f6bb7ac9c5b94d32dc84dfa967a0f7c4
View Raw JSON Data
{
  "op": [
    "comment",
    {
      "body": "Congratulations @sanderpick! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :\n\n<table><tr><td>https://steemitimages.com/60x60/http://steemitboard.com/notifications/firstpost.png</td><td>You published your First Post</td></tr>\n<tr><td>https://steemitimages.com/60x60/http://steemitboard.com/notifications/firstvoted.png</td><td>You got a First Vote</td></tr>\n<tr><td>https://steemitimages.com/60x60/http://steemitboard.com/notifications/firstcomment.png</td><td>You made your First Comment</td></tr>\n<tr><td>https://steemitimages.com/60x70/http://steemitboard.com/@sanderpick/voted.png?201811012312</td><td>You received more than 10 upvotes. Your next target is to reach 50 upvotes.</td></tr>\n<tr><td>https://steemitimages.com/60x60/http://steemitboard.com/notifications/firstvote.png</td><td>You made your First Vote</td></tr>\n</table>\n\n<sub>_[Click here to view your Board of Honor](https://steemitboard.com/@sanderpick)_</sub>\n<sub>_If you no longer want to receive notifications, reply to this comment with the word_ `STOP`</sub>\n\n\n\n**Do not miss the last post from @steemitboard:**\n<table><tr><td><a href=\"https://steemit.com/steemfest/@steemitboard/i06trehc\"><img src=\"https://steemitimages.com/64x128/https://ipfs.io/ipfs/QmU34ZrY632FFKQ1vbrkSM27VcnsjQdtXPynfMrpxDFJcF\"></a></td><td><a href=\"https://steemit.com/steemfest/@steemitboard/i06trehc\">Be ready for the next contest!</a></td></tr><tr><td><a href=\"https://steemit.com/halloween/@steemitboard/trick-or-treat-publish-your-scariest-halloweeen-story-and-win-a-new-badge\"><img src=\"https://steemitimages.com/64x128/http://i.cubeupload.com/RUyB3u.png\"></a></td><td><a href=\"https://steemit.com/halloween/@steemitboard/trick-or-treat-publish-your-scariest-halloweeen-story-and-win-a-new-badge\">Trick or Treat - Publish your scariest halloween story and win a new badge</a></td></tr></table>\n\n> Support [SteemitBoard's project](https://steemit.com/@steemitboard)! **[Vote for its witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1)** and **get one more award**!",
      "title": "",
      "author": "steemitboard",
      "permlink": "steemitboard-notify-sanderpick-20181102t005053000z",
      "json_metadata": "{\"image\":[\"https://steemitboard.com/img/notify.png\"]}",
      "parent_author": "sanderpick",
      "parent_permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol"
    }
  ],
  "block": 27333226,
  "trx_id": "143d4e15f6bb7ac9c5b94d32dc84dfa967a0f7c4",
  "op_in_trx": 0,
  "timestamp": "2018-11-02T00:50:57",
  "virtual_op": false,
  "trx_in_block": 0
}
2018/11/01 23:33:00
votersanderpick
authorsanderpick
weight2066 (20.66%)
rshares1083231985
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
pending payout0.053 HBD
total vote weight218211
Transaction InfoBlock #27331668/Trx 16179551cedbe166c080026217920f24917d73f7
View Raw JSON Data
{
  "op": [
    "effective_comment_vote",
    {
      "voter": "sanderpick",
      "author": "sanderpick",
      "weight": 2066,
      "rshares": 1083231985,
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol",
      "pending_payout": "0.053 HBD",
      "total_vote_weight": 218211
    }
  ],
  "block": 27331668,
  "trx_id": "16179551cedbe166c080026217920f24917d73f7",
  "op_in_trx": 1,
  "timestamp": "2018-11-01T23:33:00",
  "virtual_op": true,
  "trx_in_block": 13
}
2018/11/01 23:33:00
votersanderpick
authorsanderpick
weight10000 (100.00%)
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
Transaction InfoBlock #27331668/Trx 16179551cedbe166c080026217920f24917d73f7
View Raw JSON Data
{
  "op": [
    "vote",
    {
      "voter": "sanderpick",
      "author": "sanderpick",
      "weight": 10000,
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol"
    }
  ],
  "block": 27331668,
  "trx_id": "16179551cedbe166c080026217920f24917d73f7",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T23:33:00",
  "virtual_op": false,
  "trx_in_block": 13
}
2018/11/01 23:32:36
votersanderpick
authorandrewxhill
weight22782
rshares1106293619
permlinkintroducing-textile-a-decentralized-end-to-end-encrypted-photo-sharing-app-built-on-ipfs
pending payout0.001 HBD
total vote weight35555
Transaction InfoBlock #27331660/Trx 9c9cdd424b774741bb4093e496331f6ede04630b
View Raw JSON Data
{
  "op": [
    "effective_comment_vote",
    {
      "voter": "sanderpick",
      "author": "andrewxhill",
      "weight": 22782,
      "rshares": 1106293619,
      "permlink": "introducing-textile-a-decentralized-end-to-end-encrypted-photo-sharing-app-built-on-ipfs",
      "pending_payout": "0.001 HBD",
      "total_vote_weight": 35555
    }
  ],
  "block": 27331660,
  "trx_id": "9c9cdd424b774741bb4093e496331f6ede04630b",
  "op_in_trx": 1,
  "timestamp": "2018-11-01T23:32:36",
  "virtual_op": true,
  "trx_in_block": 9
}
2018/11/01 23:32:36
votersanderpick
authorandrewxhill
weight10000 (100.00%)
permlinkintroducing-textile-a-decentralized-end-to-end-encrypted-photo-sharing-app-built-on-ipfs
Transaction InfoBlock #27331660/Trx 9c9cdd424b774741bb4093e496331f6ede04630b
View Raw JSON Data
{
  "op": [
    "vote",
    {
      "voter": "sanderpick",
      "author": "andrewxhill",
      "weight": 10000,
      "permlink": "introducing-textile-a-decentralized-end-to-end-encrypted-photo-sharing-app-built-on-ipfs"
    }
  ],
  "block": 27331660,
  "trx_id": "9c9cdd424b774741bb4093e496331f6ede04630b",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T23:32:36",
  "virtual_op": false,
  "trx_in_block": 9
}
2018/11/01 23:31:57
idfollow
json["follow",{"follower":"sanderpick","following":"andrewxhill","what":["blog"]}]
required auths[]
required posting auths["sanderpick"]
Transaction InfoBlock #27331647/Trx 86c9736eefc91aad041befe5dbb9baa96a0b3738
View Raw JSON Data
{
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"sanderpick\",\"following\":\"andrewxhill\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "sanderpick"
      ]
    }
  ],
  "block": 27331647,
  "trx_id": "86c9736eefc91aad041befe5dbb9baa96a0b3738",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T23:31:57",
  "virtual_op": false,
  "trx_in_block": 14
}
2018/11/01 22:32:03
voternewforyou
authorsanderpick
weight76790
rshares25251878202
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
pending payout0.051 HBD
total vote weight216145
Transaction InfoBlock #27330449/Trx c213e988147d1a782a982c0d1d7d415e0005f5a0
View Raw JSON Data
{
  "op": [
    "effective_comment_vote",
    {
      "voter": "newforyou",
      "author": "sanderpick",
      "weight": 76790,
      "rshares": 25251878202,
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol",
      "pending_payout": "0.051 HBD",
      "total_vote_weight": 216145
    }
  ],
  "block": 27330449,
  "trx_id": "c213e988147d1a782a982c0d1d7d415e0005f5a0",
  "op_in_trx": 1,
  "timestamp": "2018-11-01T22:32:03",
  "virtual_op": true,
  "trx_in_block": 21
}
2018/11/01 22:32:03
voternewforyou
authorsanderpick
weight10000 (100.00%)
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
Transaction InfoBlock #27330449/Trx c213e988147d1a782a982c0d1d7d415e0005f5a0
View Raw JSON Data
{
  "op": [
    "vote",
    {
      "voter": "newforyou",
      "author": "sanderpick",
      "weight": 10000,
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol"
    }
  ],
  "block": 27330449,
  "trx_id": "c213e988147d1a782a982c0d1d7d415e0005f5a0",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T22:32:03",
  "virtual_op": false,
  "trx_in_block": 21
}
2018/11/01 21:54:12
voterjosexavier
authorsanderpick
weight2029 (20.29%)
rshares531797410
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
pending payout0.022 HBD
total vote weight139355
Transaction InfoBlock #27329692/Trx 5850c6cd7ac78a999ed6bafc7a1664354bdb5532
View Raw JSON Data
{
  "op": [
    "effective_comment_vote",
    {
      "voter": "josexavier",
      "author": "sanderpick",
      "weight": 2029,
      "rshares": 531797410,
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol",
      "pending_payout": "0.022 HBD",
      "total_vote_weight": 139355
    }
  ],
  "block": 27329692,
  "trx_id": "5850c6cd7ac78a999ed6bafc7a1664354bdb5532",
  "op_in_trx": 1,
  "timestamp": "2018-11-01T21:54:12",
  "virtual_op": true,
  "trx_in_block": 23
}
2018/11/01 21:54:12
voterjosexavier
authorsanderpick
weight10000 (100.00%)
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
Transaction InfoBlock #27329692/Trx 5850c6cd7ac78a999ed6bafc7a1664354bdb5532
View Raw JSON Data
{
  "op": [
    "vote",
    {
      "voter": "josexavier",
      "author": "sanderpick",
      "weight": 10000,
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol"
    }
  ],
  "block": 27329692,
  "trx_id": "5850c6cd7ac78a999ed6bafc7a1664354bdb5532",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T21:54:12",
  "virtual_op": false,
  "trx_in_block": 23
}
2018/11/01 21:52:06
bodyHere's an intro to Textile from @andrewxhill: We've been in private beta through the Fall and are getting very close to a public release. We wanted to invite Steem'ers to give it an early try though. Please keep in mind that it's Beta, the app is still changing a lot between each release. We also have a major migration planned in a week in order to get users a bunch of cool new features. ![IMG_0844.png](https://cdn.steemitimages.com/DQmZveBGwjTs1LviXkneXsBPXTwe12JHDRfchAe9XWfNigH/IMG_0844.png) Brief overview: - Textile runs an IPFS node directly on your mobile device - Photos you add to the app will be privately encrypted with a key only you have access to, and hashed for IPFS. - You can create Threads, which are small groups to share photos with, you all share a set of keys specific to the Thread. - When you post a photo to a Thread, you share the photo key to Thread members so they can fetch it and view it. Here's another technical post on our encryption: [The 5 steps to end-to-end encrypted photo storage and sharing](https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14) If you are still interested, you can join me with the referral code, 'OCCER' Just enter it after you download the app for iOS or Android. Then, after you have the app up and running, click the following link from your phone to join my Steem Thread to test out sharing. https://www.textile.photos/invites/new#id=QmNxaZpX2EvsJ9GGvxzqHBdRnzF5GkGNFeX3fRX2n7N4FJ&key=1COZFJ77NuJhCeOuoIdlf8n8ajU1COZFCR0fSKN3fGcr&inviter=andrewxhill&name=Steem&referral=MSCES
title
authorsanderpick
permlinkre-sanderpick-hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol-20181101t215203511z
json metadata{"tags":["introduceyourself"],"users":["andrewxhill"],"image":["https://cdn.steemitimages.com/DQmZveBGwjTs1LviXkneXsBPXTwe12JHDRfchAe9XWfNigH/IMG_0844.png"],"links":["https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14","https://www.textile.photos/invites/new#id=QmNxaZpX2EvsJ9GGvxzqHBdRnzF5GkGNFeX3fRX2n7N4FJ&amp;key=1COZFJ77NuJhCeOuoIdlf8n8ajU1COZFCR0fSKN3fGcr&amp;inviter=andrewxhill&amp;name=Steem&amp;referral=MSCES"],"app":"steemit/0.1"}
parent authorsanderpick
parent permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
Transaction InfoBlock #27329650/Trx 4ccb25e7c408f9c8d9609c5a2c6c8d391a8c2e38
View Raw JSON Data
{
  "op": [
    "comment",
    {
      "body": "Here's an intro to Textile from @andrewxhill:\n\nWe've been in private beta through the Fall and are getting very close to a public release. We wanted to invite Steem'ers to give it an early try though. Please keep in mind that it's Beta, the app is still changing a lot between each release. We also have a major migration planned in a week in order to get users a bunch of cool new features.\n\n![IMG_0844.png](https://cdn.steemitimages.com/DQmZveBGwjTs1LviXkneXsBPXTwe12JHDRfchAe9XWfNigH/IMG_0844.png)\n\nBrief overview:\n- Textile runs an IPFS node directly on your mobile device\n- Photos you add to the app will be privately encrypted with a key only you have access to, and hashed for IPFS.\n- You can create Threads, which are small groups to share photos with, you all share a set of keys specific to the Thread.\n- When you post a photo to a Thread, you share the photo key to Thread members so they can fetch it and view it.\n\nHere's another technical post on our encryption: [The 5 steps to end-to-end encrypted photo storage and sharing](https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14)\n\nIf you are still interested, you can join me with the referral code, 'OCCER'\n\nJust enter it after you download the app for iOS or Android.\n\nThen, after you have the app up and running, click the following link from your phone to join my Steem Thread to test out sharing.\n\nhttps://www.textile.photos/invites/new#id=QmNxaZpX2EvsJ9GGvxzqHBdRnzF5GkGNFeX3fRX2n7N4FJ&key=1COZFJ77NuJhCeOuoIdlf8n8ajU1COZFCR0fSKN3fGcr&inviter=andrewxhill&name=Steem&referral=MSCES",
      "title": "",
      "author": "sanderpick",
      "permlink": "re-sanderpick-hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol-20181101t215203511z",
      "json_metadata": "{\"tags\":[\"introduceyourself\"],\"users\":[\"andrewxhill\"],\"image\":[\"https://cdn.steemitimages.com/DQmZveBGwjTs1LviXkneXsBPXTwe12JHDRfchAe9XWfNigH/IMG_0844.png\"],\"links\":[\"https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14\",\"https://www.textile.photos/invites/new#id=QmNxaZpX2EvsJ9GGvxzqHBdRnzF5GkGNFeX3fRX2n7N4FJ&amp;key=1COZFJ77NuJhCeOuoIdlf8n8ajU1COZFCR0fSKN3fGcr&amp;inviter=andrewxhill&amp;name=Steem&amp;referral=MSCES\"],\"app\":\"steemit/0.1\"}",
      "parent_author": "sanderpick",
      "parent_permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol"
    }
  ],
  "block": 27329650,
  "trx_id": "4ccb25e7c408f9c8d9609c5a2c6c8d391a8c2e38",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T21:52:06",
  "virtual_op": false,
  "trx_in_block": 10
}
2018/11/01 21:41:57
voterandrewxhill
authorsanderpick
weight333 (3.33%)
rshares152762950
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
pending payout0.021 HBD
total vote weight137326
Transaction InfoBlock #27329447/Trx da53d9ce4f26483940b49df744d038a0e49c3b4e
View Raw JSON Data
{
  "op": [
    "effective_comment_vote",
    {
      "voter": "andrewxhill",
      "author": "sanderpick",
      "weight": 333,
      "rshares": 152762950,
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol",
      "pending_payout": "0.021 HBD",
      "total_vote_weight": 137326
    }
  ],
  "block": 27329447,
  "trx_id": "da53d9ce4f26483940b49df744d038a0e49c3b4e",
  "op_in_trx": 1,
  "timestamp": "2018-11-01T21:41:57",
  "virtual_op": true,
  "trx_in_block": 2
}
2018/11/01 21:41:57
voterandrewxhill
authorsanderpick
weight10000 (100.00%)
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
Transaction InfoBlock #27329447/Trx da53d9ce4f26483940b49df744d038a0e49c3b4e
View Raw JSON Data
{
  "op": [
    "vote",
    {
      "voter": "andrewxhill",
      "author": "sanderpick",
      "weight": 10000,
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol"
    }
  ],
  "block": 27329447,
  "trx_id": "da53d9ce4f26483940b49df744d038a0e49c3b4e",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T21:41:57",
  "virtual_op": false,
  "trx_in_block": 2
}
2018/11/01 21:34:48
voterearncrypto
authorsanderpick
weight6268 (62.68%)
rshares13538473732
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
pending payout0.021 HBD
total vote weight136744
Transaction InfoBlock #27329304/Trx fc8d40e7b9e2c0358daf4404ee2af19589130936
View Raw JSON Data
{
  "op": [
    "effective_comment_vote",
    {
      "voter": "earncrypto",
      "author": "sanderpick",
      "weight": 6268,
      "rshares": 13538473732,
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol",
      "pending_payout": "0.021 HBD",
      "total_vote_weight": 136744
    }
  ],
  "block": 27329304,
  "trx_id": "fc8d40e7b9e2c0358daf4404ee2af19589130936",
  "op_in_trx": 1,
  "timestamp": "2018-11-01T21:34:48",
  "virtual_op": true,
  "trx_in_block": 3
}
2018/11/01 21:34:48
voterearncrypto
authorsanderpick
weight4000 (40.00%)
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
Transaction InfoBlock #27329304/Trx fc8d40e7b9e2c0358daf4404ee2af19589130936
View Raw JSON Data
{
  "op": [
    "vote",
    {
      "voter": "earncrypto",
      "author": "sanderpick",
      "weight": 4000,
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol"
    }
  ],
  "block": 27329304,
  "trx_id": "fc8d40e7b9e2c0358daf4404ee2af19589130936",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T21:34:48",
  "virtual_op": false,
  "trx_in_block": 3
}
2018/11/01 21:34:42
bodyWelcome sanderpick! <br>**eSteem** is the application that improves your experience here. We have Mobile application for [Android](https://play.google.com/store/apps/details?id=com.netsolutions.esteem) and [iOS](https://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=1141397898&mt=8) users. We also have developed [Surfer Desktop](https://github.com/esteemapp/esteem-surfer/releases) application that helps you to gain new followers and stay connected with your friends, unique features - notifications, bookmarks, favorites, drafts, and more. <br> We reward our users with encouragement upvotes as well as monthly giveaways rewarding Spotlight top users and active Discord users. <br>Learn more: https://esteem.app <br>Join our discord: https://discord.gg/8eHupPq
title
authoresteemapp
permlinkre-2018111t163439830z
json metadata{"tags":["esteem"],"app":"esteem/1.0-welcome","format":"markdown+html","community":"esteem.app"}
parent authorsanderpick
parent permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
Transaction InfoBlock #27329302/Trx bc18b5549fb764dde011b7e125f0795d921e3964
View Raw JSON Data
{
  "op": [
    "comment",
    {
      "body": "Welcome sanderpick! <br>**eSteem** is the application that improves your experience here. We have Mobile application for [Android](https://play.google.com/store/apps/details?id=com.netsolutions.esteem) and [iOS](https://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=1141397898&mt=8) users. We also have developed [Surfer Desktop](https://github.com/esteemapp/esteem-surfer/releases) application that helps you to gain new followers and stay connected with your friends, unique features - notifications, bookmarks, favorites, drafts, and more. <br> We reward our users with encouragement upvotes as well as monthly giveaways rewarding Spotlight top users and active Discord users. <br>Learn more: https://esteem.app <br>Join our discord: https://discord.gg/8eHupPq",
      "title": "",
      "author": "esteemapp",
      "permlink": "re-2018111t163439830z",
      "json_metadata": "{\"tags\":[\"esteem\"],\"app\":\"esteem/1.0-welcome\",\"format\":\"markdown+html\",\"community\":\"esteem.app\"}",
      "parent_author": "sanderpick",
      "parent_permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol"
    }
  ],
  "block": 27329302,
  "trx_id": "bc18b5549fb764dde011b7e125f0795d921e3964",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T21:34:42",
  "virtual_op": false,
  "trx_in_block": 31
}
2018/11/01 21:33:39
body<center> Welcome to Steem @sanderpick. <br/><br/>Do read [A thumb rule for steemit minnows - 50:100:200:25](https://steemit.com/steemit/@steemladder/a-thumb-rule-for-steemit-minnows-50-100-200-25) for starter tips. <br/> <br/>Spend time reading [Steem Blue Paper](https://steem.io/steem-bluepaper.pdf) to know how Steem blockchain works and if you still have any queries ask them on our [Ask me anything about Steemit post](https://steemit.com/steem/@steemladder/there-are-no-dumb-questions-ask-anything-you-want-to-know-about-steem-blockchain) and we will try to answer that.<br/><br/>All the Best!!! </center>
titleWelcome to Steem!
authorsteemladder
permlinkre-sanderpick-hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol-20181101t213326545z
json metadata{"tags":["steemladder"]}
parent authorsanderpick
parent permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
Transaction InfoBlock #27329281/Trx a2693b27f74590f887214879b98ddf333338f3aa
View Raw JSON Data
{
  "op": [
    "comment",
    {
      "body": "<center>\nWelcome to Steem @sanderpick. <br/><br/>Do read [A thumb rule for steemit minnows - 50:100:200:25](https://steemit.com/steemit/@steemladder/a-thumb-rule-for-steemit-minnows-50-100-200-25) for starter tips. <br/> <br/>Spend time reading [Steem Blue Paper](https://steem.io/steem-bluepaper.pdf) to know how Steem blockchain works and if you still have any queries ask them on our [Ask me anything about Steemit post](https://steemit.com/steem/@steemladder/there-are-no-dumb-questions-ask-anything-you-want-to-know-about-steem-blockchain) and we will try to answer that.<br/><br/>All the Best!!!\n</center>",
      "title": "Welcome to Steem!",
      "author": "steemladder",
      "permlink": "re-sanderpick-hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol-20181101t213326545z",
      "json_metadata": "{\"tags\":[\"steemladder\"]}",
      "parent_author": "sanderpick",
      "parent_permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol"
    }
  ],
  "block": 27329281,
  "trx_id": "a2693b27f74590f887214879b98ddf333338f3aa",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T21:33:39",
  "virtual_op": false,
  "trx_in_block": 12
}
2018/11/01 21:33:39
bodyHi! I am a robot. I just upvoted you! I found similar content that readers might be interested in: https://medium.com/textileio/wip-textile-threads-whitepaper-just-kidding-6ce3a6624338
title
authorcheetah
permlinkcheetah-re-sanderpickhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
json metadata
parent authorsanderpick
parent permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
Transaction InfoBlock #27329281/Trx 4ea71888a802c39c2dd946a87202ca4d75760b76
View Raw JSON Data
{
  "op": [
    "comment",
    {
      "body": "Hi! I am a robot. I just upvoted you! I found similar content that readers might be interested in:\nhttps://medium.com/textileio/wip-textile-threads-whitepaper-just-kidding-6ce3a6624338",
      "title": "",
      "author": "cheetah",
      "permlink": "cheetah-re-sanderpickhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol",
      "json_metadata": "",
      "parent_author": "sanderpick",
      "parent_permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol"
    }
  ],
  "block": 27329281,
  "trx_id": "4ea71888a802c39c2dd946a87202ca4d75760b76",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T21:33:39",
  "virtual_op": false,
  "trx_in_block": 10
}
2018/11/01 21:33:33
votersteemladder
authorsanderpick
weight737 (7.37%)
rshares4854474170
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
pending payout0.005 HBD
total vote weight71893
Transaction InfoBlock #27329279/Trx 25667ed205a7a63181695710cfdbc8536d29f305
View Raw JSON Data
{
  "op": [
    "effective_comment_vote",
    {
      "voter": "steemladder",
      "author": "sanderpick",
      "weight": 737,
      "rshares": 4854474170,
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol",
      "pending_payout": "0.005 HBD",
      "total_vote_weight": 71893
    }
  ],
  "block": 27329279,
  "trx_id": "25667ed205a7a63181695710cfdbc8536d29f305",
  "op_in_trx": 1,
  "timestamp": "2018-11-01T21:33:33",
  "virtual_op": true,
  "trx_in_block": 15
}
2018/11/01 21:33:33
votersteemladder
authorsanderpick
weight500 (5.00%)
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
Transaction InfoBlock #27329279/Trx 25667ed205a7a63181695710cfdbc8536d29f305
View Raw JSON Data
{
  "op": [
    "vote",
    {
      "voter": "steemladder",
      "author": "sanderpick",
      "weight": 500,
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol"
    }
  ],
  "block": 27329279,
  "trx_id": "25667ed205a7a63181695710cfdbc8536d29f305",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T21:33:33",
  "virtual_op": false,
  "trx_in_block": 15
}
2018/11/01 21:33:30
votercheetah
authorsanderpick
weight165 (1.65%)
rshares273810282
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
pending payout0.000 HBD
total vote weight16548
Transaction InfoBlock #27329278/Trx 380c9aea11b3999de4171de5b8324872c70c8206
View Raw JSON Data
{
  "op": [
    "effective_comment_vote",
    {
      "voter": "cheetah",
      "author": "sanderpick",
      "weight": 165,
      "rshares": 273810282,
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol",
      "pending_payout": "0.000 HBD",
      "total_vote_weight": 16548
    }
  ],
  "block": 27329278,
  "trx_id": "380c9aea11b3999de4171de5b8324872c70c8206",
  "op_in_trx": 1,
  "timestamp": "2018-11-01T21:33:30",
  "virtual_op": true,
  "trx_in_block": 1
}
2018/11/01 21:33:30
votercheetah
authorsanderpick
weight8 (0.08%)
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
Transaction InfoBlock #27329278/Trx 380c9aea11b3999de4171de5b8324872c70c8206
View Raw JSON Data
{
  "op": [
    "vote",
    {
      "voter": "cheetah",
      "author": "sanderpick",
      "weight": 8,
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol"
    }
  ],
  "block": 27329278,
  "trx_id": "380c9aea11b3999de4171de5b8324872c70c8206",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T21:33:30",
  "virtual_op": false,
  "trx_in_block": 1
}
2018/11/01 21:33:21
body<html> <p><em>Written by </em><a href="https://medium.com/@carsonfarmer"><em>Carson Farmer</em></a><em> &amp; </em><a href="https://medium.com/@sanderpick"><em>Sander Pick</em></a></p> <p><center><img src="https://cdn-images-1.medium.com/max/800/1*beyK889WqmNEatN55oBAnA.png" width="800" height="565"/><em>Download the app, take a picture, share!</em></center></p> <p>Recently, &nbsp;we’ve started writing more about the technologies underlying Textile &nbsp;Photos that help keep your photos (and likes and comments, etc) safe and &nbsp;secure on the decentralized web. In our previous post, we talked about <a href="https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14">the encryption process Textile Photos</a>, &nbsp;with a focus how Textile delivers end-to-end encrypted photo sharing. &nbsp;Today’s post is a follow-up (though it should also be sufficiently &nbsp;detailed to stand on its own), this time highlighting how Textile &nbsp;coordinates private photo sharing among groups of users, a feature we &nbsp;call <em>Threads</em>.</p> <p><strong>Why we built it</strong><br> We &nbsp;designed Threads to allow groups of users to share photos securely and &nbsp;privately, without any centralized, authoritative database. We also made &nbsp;sure it all works well offline, that its possible to recover lost data, &nbsp;and that its easy to add new members.</p> <p><strong>What makes Threads different</strong><br> Threads &nbsp;allow private groups to post photos and interact over a decentralized &nbsp;network, maintaining complete control over their own content. Textile &nbsp;operates in a completely zero-knowledge framework. Private by design.</p> <p><strong>Why Threads are exciting</strong><br> Because &nbsp;photos are just the first step. Today, Threads allow users to share a &nbsp;photo with other Thread in a secure, decentralized way. Threads can &nbsp;facilitate secure sharing, coordination, and storage of <em>many</em> types of data over a decentralized network. Upgradable by design.</p> <p>On &nbsp;the surface, you can think of each Thread like a decentralized &nbsp;database, shared between specific participants. We built Threads into &nbsp;the fabric of Textile (see what we did there 😉) because group members &nbsp;need a record of who shared what photo, and when. But, once we created &nbsp;Threads, we realized just how powerful a concept this was — for those &nbsp;familiar with mobile app development, think <a href="https://realm.io/">Realm</a> or <a href="https://firebase.google.com/">Firebase</a> but <em>without</em> the centralized server.</p> <p>To &nbsp;really understand what Threads brings to the table, you really need to &nbsp;understand Threads themselves. So let’s dig a bit deeper into how &nbsp;Textile conceptualizes and implements Threads, and how that helps keep &nbsp;your photos (and likes and comments, etc) safe and secure on the &nbsp;decentralized web. We’ll start by highlighting the specific requirements &nbsp;we had when developing Threads, and then break down each of these &nbsp;requirements into the specific solutions that we came up with. Along the &nbsp;way, our CTO Sander Pick will highlight how those various solutions &nbsp;came about, and why we think our approach is in the best interest of our &nbsp;users.</p> <h3>The experience</h3> <p><a href="https://textile.photos/">Textile Photos</a> &nbsp;allows small, decentralized, private groups to share photos, send &nbsp;messages, and engage with each other. That’s the experience, so it has &nbsp;to ‘just work’.</p> <p><center><img src="https://cdn-images-1.medium.com/max/800/1*xEE3Pp-LdU_r8qsPvMVS8w.gif" width="400" height="600"/></center></p> <h3>Requirements</h3> <p>To drive the Textile user experience, we identified five key features needed in the sharing protocols:</p> <ol> <li><strong>A mechanism to share and receive state updates within a group of n users</strong><br> To &nbsp;enable photo sharing (and other common interactions such as likes and &nbsp;comments) among a group of friends and/or colleagues, some concept of a <em>shared state</em> is required.</li> <li><strong>A way to ensure the shared state stays resilient to peers dropping out or latency issues</strong><br> Since &nbsp;we’re operating in a mobile environment, we have to expect peers to &nbsp;continually drop ‘offline’ due to coverage issues, app back-grounding, &nbsp;battery optimizations, and a whole slew of other reasons for a mobile &nbsp;device to be cut off from a network.</li> <li><strong>A way to avoid state conflicts with other members of the group</strong><br> On &nbsp;top of the requirements above, when peers do come back online, we don’t &nbsp;want any state changes that were made by other members of the group &nbsp;while they were disconnected from the network to conflict with their own &nbsp;local changes.</li> <li><strong>A mechanism to recover the full state from the network as a whole</strong><br> Another &nbsp;important consideration in the mobile world is that the number of users &nbsp;(out of n) that are online at any given time is generally unknown, and &nbsp;quite possibly zero. To reiterate, we want a decentralized shared state, &nbsp;but it has to work <em>even when you are the only member online</em>. &nbsp;This means we have to assume the full group state may not ever be &nbsp;directly accessible (i.e., downloadable) from a single group member. &nbsp;This is in contrast to something like <a href="https://en.wikipedia.org/wiki/Bitcoin">Bitcoin</a>, where new nodes are able to <a href="https://bitcoin.org/en/developer-guide#initial-block-download">download the full blockchain</a> from any connected peer.</li> <li><strong>Way to link updates via their content, rather than where they are stored</strong><br> Since we are building on top of the IPFS network, and would like to eventually support a <a href="https://filecoin.io/">Filecoin</a>-based &nbsp;future in which users can select from a multitude of decentralized &nbsp;storage providers, Threads need to embrace content addressing, rather &nbsp;than location addressing. This makes it easy to grow and change the &nbsp;underlying network, without affecting data access and sharing.</li> </ol> <p>With these requirements in mind, let’s break down our solutions into their individual components…</p> <h3>Solutions</h3> <h4>1. Handling Updates — use a peer-to-peer network with structured updates</h4> <p>First things first: <em>how do we handle state updates between a set of distributed peers?</em> This is mostly about <a href="https://en.wikipedia.org/wiki/Peer-to-peer">peer-to-peer (p2p) networking</a>. &nbsp;And when it comes to communicating between heterogeneous network &nbsp;devices (computers, phones, IoT devices, etc), we actually need many <a href="https://en.wikipedia.org/wiki/Lists_of_network_protocols">different types of network protocols</a>. &nbsp;That way, no matter what type of device we are talking about — be it a &nbsp;phone, desktop computer, browser, or Internet-enabled fridge — it is &nbsp;able to communicate with other devices located in the same room, or on &nbsp;the other side of the planet.</p> <p>At Textile, we use the super amazing <a href="https://libp2p.io/">libp2p</a> &nbsp;library for our networking needs. Libp2p is a networking stack and &nbsp;library (you might have heard it called a protocol suite) modularized &nbsp;out of the <a href="https://ipfs.io/">IPFS project</a>, &nbsp;and bundled separately for other tools to use. Essentially, libp2p does &nbsp;all the heavy network lifting so that we can focus on our core task: &nbsp;exchanging updates between communicating peers.</p> <p><center><img src="https://cdn-images-1.medium.com/max/800/1*0iCOZW6HrpdQ0izG5O9ciw.png" width="800" height="316"/><em>Decentralized, peer-to-peer networks are radically different types of communication networks.</em></center></p> <blockquote>Libp2p &nbsp;was a pretty natural choice for us. The stack includes all the crypto &nbsp;and networking protocols we need to deliver messages to group members, &nbsp;and the libp2p developer community is super responsive and excited about &nbsp;the power of p2p interactions. Easy choice.</blockquote> <p>The &nbsp;other really nice thing about using the libp2p library is it comes &nbsp;packed with many useful cryptography tools and functions, keeping &nbsp;communications secure. For instance, all p2p communications over the &nbsp;Textile network use the <a href="https://github.com/libp2p/go-libp2p-secio">secio</a> <a href="https://github.com/libp2p/go-stream-security">stream security transport</a>. &nbsp;This way, all connections use secure sessions provided by libp2p/secio &nbsp;to encrypt all traffic, whereby a TLS-like handshake is used to setup &nbsp;the initial communication channel.</p> <p>Like many IPFS-based projects, Textile uses <a href="https://developers.google.com/protocol-buffers/">Protocol Buffers</a> &nbsp;for over-the-wire communication, and advanced cryptographic algorithms &nbsp;to secure those messages. Essentially, each update to the shared group &nbsp;state is just an encrypted Profobuf message with two parts: a header &nbsp;with author and date info, and a body with the type-specific data. These &nbsp;pieces are sent in their own inner-’envelope’ which contains a link to &nbsp;the encrypted message and the Thread ID. This inner-envelope is then &nbsp;signed by the sender and placed into the wire ‘envelope’ along with it’s &nbsp;signature. You can read more about some of the cryptographic tools &nbsp;Textile uses in <a href="https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14">this previous article</a>. You can also check out how we structure our <a href="https://github.com/textileio/textile-go/blob/master/pb/protos/thread.proto">Protobuf messages</a>, learn a bit more about <a href="https://github.com/auditdrivencrypto/secure-channel/blob/master/prior-art.md#ipfss-secure-channel">how secio works</a>, plus check out some <a href="https://github.com/textileio/textile-go/commit/05a269cd5dd72da224c4d2c472abad00a078d4ca">recent updates</a> to message encryption while you’re at it.</p> <h4>2. Network Resilience — support offline messaging so peers can come and&nbsp;go</h4> <p>If you are at all familiar with libp2p, then you might be thinking <em>“ah libp2p has a pubsub layer that would be perfect for exchanging updates to a group of connecting peers”</em>. &nbsp;And while you’d certainly be right, there are a few key limitations &nbsp;that makes using pubsub for something like Textile Photos pretty &nbsp;cumbersome. On top of this, while pubsub is super nice for things like &nbsp;chat rooms or distributed services, it is a ‘fire-and-forget’ messaging &nbsp;protocol, meaning that once a peer publishes a message, it is up to its &nbsp;peers to ensure they are listening for the right message at the right &nbsp;time. To circumvent this, some pubsub systems introduce message echoing, &nbsp;to ensure a message stays in the system long enough to be picked up by &nbsp;the peers who might need it. However, this can lead to really noisy &nbsp;network traffic, and is really just a band-aid over a larger issue.</p> <blockquote>Our &nbsp;initial POC involved pubsub and always-online room echoers… not &nbsp;scalable or particularly decentralized. A real solution to distributing &nbsp;state has to involve direct messaging with an offline mechanism.</blockquote> <p>So this starts to get at our second requirement, that <em>the shared state stays resilient to peers dropping out</em>. &nbsp;We need to assume peers might not be around to receive important &nbsp;messages in ‘real-time’, which is a common problem with p2p systems. &nbsp;Right now, Textile addresses this problem by enabling what you might &nbsp;call <em>offline messaging</em>. Since we’re <a href="https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14#ea20">already using IPFS for data storage and communication</a>, &nbsp;we wanted to take advantage of some of the core technologies driving &nbsp;IPFS. In particular, we (currently) use a special fork of the <a href="https://en.wikipedia.org/wiki/Kademlia">Kademlia</a>-based <a href="https://en.wikipedia.org/wiki/Distributed_hash_table">distributed hash table</a> (DHT) <a href="https://github.com/libp2p/go-libp2p-kad-dht">used by IPFS, </a>that allows us to post messages for a peer directly in the DHT. For those unfamiliar with DHTs, they are a <a href="https://en.wikipedia.org/wiki/Hash_table">hash table</a> &nbsp;where the data is spread across a network of nodes or peers. And these &nbsp;peers are all coordinated to enable efficient access and lookup between &nbsp;nodes in a decentralized way. You can read more about this kind of stuff &nbsp;in our previous article about <a href="https://medium.com/textileio/swapping-bits-and-distributing-hashes-on-the-decentralized-web-5da98a3507">how IPFS peers find, request, and retrieve content (and each other) on the decentralized web</a>. &nbsp;So, when a peer we want to communicate with is offline, rather than &nbsp;blindly sending them a message that will never be received, we post a &nbsp;message to Textile’s DHT, and they can then retrieve that message the &nbsp;next time they come online again. Conceptually simple, and works pretty &nbsp;well in practice.</p> <p><center><img src="https://cdn-images-1.medium.com/max/800/1*JsXZAveyMui2izg_-FMh0g.png" width="800" height="485"/><em>p2p &nbsp;network with custom Textile DHT overlay. Peers post (key, value) &nbsp;messages (value) with a key specific to their intended recipient, and &nbsp;this key is broadcast and available to entire network; though only the &nbsp;intended recipient is able to decrypt the actual message content. Based &nbsp;on Figure 1–2 from </em><a href="https://www.researchgate.net/figure/2-Overlay-and-underlay-view-of-Distributed-Hash-Tables-11_fig2_304495476"><em>this&nbsp;thesis</em></a><em>.</em></center></p> <p>There &nbsp;are still some issues with our current approach, including that it is &nbsp;difficult/impossible to remove messages from the DHT manually. Indeed, &nbsp;it can start to get a bit messy when left-over offline messages have to &nbsp;be retrieved each time a peer comes back online… imagine a peer that &nbsp;goes in and out of service frequently, this could lead to a lot of &nbsp;network traffic and wasted CPU cycles. So, we’ve <a href="https://github.com/libp2p/notes/issues/2#issuecomment-433729343">implemented an alternative</a> &nbsp;to this DHT-based offline messaging system that does not suffer from &nbsp;these limitations (and also allows us to participate in the public IPFS &nbsp;network), while still remaining decentralized and scalable in the &nbsp;long-term. This new approach should be released soon, after more testing &nbsp;and evaluation. You can follow along with this progress as part of the <a href="https://github.com/textileio/textile-go/projects/2">move towards a Cafe-based setup</a> (see also <strong>What’s Next</strong>).</p> <h4>3 &amp; 4. Avoiding Conflicts &amp; State Recovery —use a CRDT to keep an immutable history across&nbsp;peers</h4> <p>Ok, so our next requirement and its associated solution have received a <a href="https://github.com/ipfs/research-CRDT/">great deal of research and development attention over the years</a>. The question of “<em>how to avoid state conflicts with other members of a group?”</em> &nbsp;comes up when working collaboratively on documents, updating shared &nbsp;databases, etc. For the purposes of updating a shared Thread of photos, &nbsp;it turns out that an <a href="https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type">operation-based CRDT</a> &nbsp;that supports append-only operations is pretty much all you need to get &nbsp;going. You can think of Textile’s CRDT (which shares some ideas with <a href="https://github.com/orbitdb/ipfs-log">ipfs-log</a>) &nbsp;setup as an immutable, append-only tree that can be used to model a &nbsp;mutable, shared state between peers. Every entry in the tree is saved on &nbsp;IPFS, and each points to a hash of previous entry(ies) forming a graph. &nbsp;These trees can be <a href="https://www.atlassian.com/git/tutorials/using-branches/git-merge">3-way and fast-forward merged</a>.</p> <p>Speaking &nbsp;of forks and joins, for those familiar with git and other similar &nbsp;system, you might be thinking this sounds a lot like a <a href="https://blog.thoughtram.io/git/2014/11/18/the-anatomy-of-a-git-commit.html#meet-the-tree-object">git hash tree</a>, <a href="https://twitter.com/Textile01/status/1004436869734543360">Merkle DAG</a>, or even a <a href="https://en.wikipedia.org/wiki/Blockchain">blockchain</a>. &nbsp;And you’d be right! The concepts are very similar, and this buys us &nbsp;some really nice properties for building and maintaining a shared state. &nbsp;By modeling our shared Thread state in this way, we benefit from tried &nbsp;and tested methods for allowing a peer to incorporate other peers’ &nbsp;updates into their state while maintaining history (via fast-forwards &nbsp;and three-way merging for example).</p> <blockquote>At the end of the day, a Thread is just a git-like hash tree of updates with a deterministic merge policy. Simple.</blockquote> <p>So &nbsp;what does this look like in practice? Currently — because things might &nbsp;change as we make improvements to the underlying implementation — each &nbsp;Thread in Textile Photos is essentially a chain of updates, where each &nbsp;update represents some specific action or event. For instance, when you &nbsp;create a new Thread, under-the-hood you are actually creating a <code>JOIN</code> update on a new Thread chain. Similarly, when you update the Thread via a new photo (<code>DATA</code> update), comment, or like (<code>ANNOTATION</code> update), you’re actually updating that Thread chain. After each modification, the <code>HEAD</code> of the Thread will point to the latest update.</p> <p>Building on top of these ideas, we also have concepts such as an <code>INVITE</code>, which points a new peer to a given point on the Thread chain, or a <code>MERGE</code>, which happens when the current <code>HEAD</code> &nbsp;is not contained in an incoming update’s parent list for some reason &nbsp;(maybe the peer doesn’t know about it because they were offline). If two &nbsp;peers are merging the <em>same sub trees</em>, &nbsp;all they need to do to ensure the update resolves to the same hash is &nbsp;a) include the same date b) exclude author info. To get the same date, &nbsp;they both follow a rule: choose the latest of the parents for the date &nbsp;(in practice they add a little bit extra on to keep it ahead of both &nbsp;parents).</p> <p>To give you a better idea of what exactly we’re talking about, consider the following set of operations: <code>User A</code> creates a new Thread, and adds a Photo. They then externally invite <code>User B</code> (sent via some other <em>secure</em> communication channel), who eventually joins the Thread. But before <code>User B</code> is able to join the Thread, <code>User A</code> adds another Photo, moving the Thread’s <code>HEAD</code> forward. By the time <code>User B</code> joins the Thread, they’d end up with a Thread sequence that looks something like this:</p> <p><center><img src="https://cdn-images-1.medium.com/max/800/1*h9eNwOauuT7mMeS4o-yGbw.png" width="800" height="478"/><em>Thread &nbsp;join example. Solid arrows point towards the ‘parent’ of a given &nbsp;update, over-the-wire communications are indicated with a 📶-style &nbsp;arrow, and messages that are rebroadcast (e.g., via the welcome message) &nbsp;are indicated with a dashed arrow. Similarly, merges point to both &nbsp;their parent&nbsp;updates.</em></center></p> <p>Here, &nbsp;we see the merge happening at the end of the sequence because the &nbsp;bottom peer is joining via an external invite that is no longer <code>HEAD</code>&nbsp;, forcing them to merge the most recent <code>DATA</code> update with their own <code>JOIN</code> update. But since merge results are deterministic (given the same parents), both peers create the <code>MERGE</code> update locally, and do not broadcast them to avoid trading merges back and forth.</p> <p>A more complete sequence is given in the following figure. Suppose <code>User A</code> &nbsp;goes ‘offline’ (e.g., their phone goes to sleep, they shut down the &nbsp;app, they lose their data connection, etc), and in the mean time, both<code> Users A</code> and <code>B</code> update the Thread, with <code>User A</code> adding an <code>ANNOTATION</code> update, and <code>User B</code> adding a new Photo (<code>DATA</code> update). Now, when <code>User A</code> comes back online, there is a conflict, and both Users create a <code>MERGE</code> update to remedy this. A <code>MERGE</code> update has two parents, in this case, the <code>DATA</code> and <code>ANNOTATION</code> update from the different users. As always, the <code>HEAD</code> continues to point to the latest update (which in the example below eventually becomes an <code>ANNOTATION</code> from <code>User B</code>). Once both peers are online again, the more straightforward update and transmit mode of operation can continue.</p> <p><center><img src="https://cdn-images-1.medium.com/max/800/1*3EuEtFHqUtALTczIeZ8_eg.png" width="800" height="473"/><em>More &nbsp;complex Thread interaction where one or more peers are temporarily &nbsp;offline. Note that an external invite is the same as a normal invite, &nbsp;but the invite details are encrypted with a single use key, which is &nbsp;sharable with the invite update location.</em></center></p> <p>The &nbsp;same properties that make hash trees or blockchains useful for &nbsp;developing a shared, consistent (consensus-driven) state, also makes it &nbsp;possible to address our fourth requirement: <em>the ability to recover the full state from the network as a whole</em>. &nbsp;Because each Thread update references its parent(s), given a single &nbsp;point on the Thread chain, we can trace back all the way to the &nbsp;beginning of the Thread. For example, at any point along the sequence in &nbsp;the above figures, a peer can trace back the history of the Thread, as &nbsp;indicated by the solid arrows. This works particularly nicely when a &nbsp;peer <code>JOIN</code>s a thread, even at a point prior to the current <code>HEAD</code>. They can simply <code>JOIN</code>, and any existing Thread member can send them the latest <code>HEAD</code> &nbsp;(even via offline messages if needed). From here, they can explore the &nbsp;entire history of the Thread with ease. This is all really similar to &nbsp;git commit speak, in which one only needs to know about a single commit &nbsp;to be able to trace back the entire history of a code project; it’s also &nbsp;essentially how blockchains work.</p> <h4>5. Content Addressing — store everything on IPFS and get ready to&nbsp;scale</h4> <p>As &nbsp;we alluded to earlier, each update to a Thread is backed by an IPFS CID &nbsp;hash (i.e., they are content addressable chunks of data on IPFS). This &nbsp;means <em>where</em> the data is stored &nbsp;is no longer relevant… IPFS will find it on the network via it’s hash. &nbsp;This helps us address our fifth requirement, that we have a <em>way to link updates via their content, rather than where they are stored</em>. We’ve covered this topic a lot <a href="https://medium.com/textileio">in the past</a>, but for the uninitiated, the next paragraph provides a summary of how content addressing on IPFS works (pulled from <a href="https://medium.com/textileio/enabling-the-distributed-web-abf7ab33b638">this previous article</a>).</p> <p>Rather than referencing a file or chunk of data by its location (think HTTP), we reference it via its <a href="https://en.wikipedia.org/wiki/Fingerprint_%28computing%29">fingerprint</a>. In IPFS and other such systems, this means identifying content by its cryptographic hash, or even better, a <a href="https://github.com/ipld/cid"><em>self-describing </em>content-addressed identifier</a> (<a href="https://github.com/multiformats/multihash">multihash</a>). A cryptographic hash is a (relatively) short alphanumeric string that’s calculated by running your content through a <a href="https://en.wikipedia.org/wiki/Cryptographic_hash_function">cryptographic hash function</a> (like <a href="https://en.wikipedia.org/wiki/SHA-3">SHA</a>). For example, when the (unencrypted) <a href="https://ipfs.io/ipfs/QmcNzGMQ1hUzSnqM2aH1Um8KWLRC4Rd9TyY4kFuYZXWXaF/Textile_Icon_500x500.png">Textile logo</a> is added to IPFS, its multihash ends up being <code>QmbgGgWW3vH7v9FDxVCzcouKGChqGEjtf6YLDUgSHnk5J2</code>. This ‘hash’ is actually the CID (Content IDentifier) for that file, computed from the <em>raw data </em>within that PNG. It is guaranteed to be cryptographically unique to the contents of <em>that </em>file, and that file only. If we change that file by even one bit, the hash will become something completely different.</p> <p>Now, &nbsp;when we want to access a file over IPFS (like the above logo), we can &nbsp;simply ask the IPFS network for the file with that exact CID, the &nbsp;network will find the peers that have the data (using a DHT), retrieve &nbsp;it, and verify (using the CID) that it’s the correct file. What this &nbsp;means is we can technically get the file from <em>multiple </em>places &nbsp;because as long as the file matches the hash, we know we’re getting the &nbsp;right data. Which brings us to the solution to our final requirement… &nbsp;use IPFS! For now, Textile is maintaining a network of large, &nbsp;homogeneous, volunteer nodes (we call them <a href="https://github.com/textileio/textile-go">Cafe</a>s) &nbsp;to ‘pin’ and store content on IPFS. It is important to note here that &nbsp;the other nodes doing the pinning are the same as the nodes on your &nbsp;phone — Textile Nodes that offer a pinning service to other peers. Soon, &nbsp;we’ll allow users to elect their own Cafe nodes, add even add &nbsp;additional nodes for redundancy. All this could eventually be driven by &nbsp;Filecoin for even greater scalablility and flexibility.</p> <h3>What’s Next?</h3> <p>So &nbsp;there you have it. Five solutions to five requirements for seamless, &nbsp;secure, decentralized photo sharing and backup. Easy 😉. And at a &nbsp;conceptual level, the Textile Thread protocol <em>is</em> &nbsp;relatively simple: blocks of operations chained together to produce a &nbsp;beautiful Thread of photos. But there’s a lot of complexity going on &nbsp;under-the-hood that has required a lot of experimentation, testing, and &nbsp;limit pushing, especially on mobile. And our journey isn’t over yet.</p> <p><a href="https://twitter.com/Textile01/lists/textile-team/members">The Textile team</a> &nbsp;is still hard at work iterating, updating, and improving upon what we &nbsp;already have working. For example, we’ll soon to moving to a new offline &nbsp;messaging system that allow us to drop the custom DHT fork, and move &nbsp;back to the public IPFS network. On top of this, our <a href="https://github.com/textileio/textile-go/projects/2">move to more powerful backup</a> &nbsp;and recovery capabilities has us taking new approaches to security, &nbsp;profile management, offline interactions, and much much more. On top of &nbsp;these changes, the team is actively working to modularize the Threads &nbsp;concept and code into its own stand-alone package, which should provide &nbsp;developers with something akin to a Realm and/or Firebase layer for &nbsp;decentralized mobile applications!</p> <p>If you are interested in learning more about this stuff, reach out over <a href="https://twitter.com/Textile01">Twitter</a> or <a href="https://slack.textile.io/">Slack</a>, &nbsp;or pull us aside the next time you see us at a conference or event. We’re happy to provide background, thoughts, and opinions on how we &nbsp;think the future of decentralized apps will play out. In the mean time, &nbsp;don’t forget to check out our <a href="https://github.com/textileio">GitHub repos</a> &nbsp;for code and PRs that showcase our current and old implementations. We &nbsp;try to make sure all our development happens out in the open, so you can &nbsp;see things as they develop. Additionally, if you haven’t already, <a href="https://www.textile.photos/#cta">don’t miss out on signing up for our waitlist</a>, where you can get early access to Textile Photos, the beautiful interface to Textile’s Threads.</p> </html>
titleHi, Steemit! We're Textile. Here's a deeper look at the tech behind our Threads protocol.
authorsanderpick
permlinkhi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol
json metadata{"tags":["introduceyourself","ipfs","libp2p","filecoin","mobile"],"image":["https://cdn-images-1.medium.com/max/800/1*beyK889WqmNEatN55oBAnA.png","https://cdn-images-1.medium.com/max/800/1*xEE3Pp-LdU_r8qsPvMVS8w.gif","https://cdn-images-1.medium.com/max/800/1*0iCOZW6HrpdQ0izG5O9ciw.png","https://cdn-images-1.medium.com/max/800/1*JsXZAveyMui2izg_-FMh0g.png","https://cdn-images-1.medium.com/max/800/1*h9eNwOauuT7mMeS4o-yGbw.png","https://cdn-images-1.medium.com/max/800/1*3EuEtFHqUtALTczIeZ8_eg.png"],"links":["https://medium.com/@carsonfarmer","https://medium.com/@sanderpick","https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14","https://realm.io/","https://firebase.google.com/","https://textile.photos/","https://en.wikipedia.org/wiki/Bitcoin","https://bitcoin.org/en/developer-guide#initial-block-download","https://filecoin.io/","https://en.wikipedia.org/wiki/Peer-to-peer","https://en.wikipedia.org/wiki/Lists_of_network_protocols","https://libp2p.io/","https://ipfs.io/","https://github.com/libp2p/go-libp2p-secio","https://github.com/libp2p/go-stream-security","https://developers.google.com/protocol-buffers/","https://github.com/textileio/textile-go/blob/master/pb/protos/thread.proto","https://github.com/auditdrivencrypto/secure-channel/blob/master/prior-art.md#ipfss-secure-channel","https://github.com/textileio/textile-go/commit/05a269cd5dd72da224c4d2c472abad00a078d4ca","https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14#ea20","https://en.wikipedia.org/wiki/Kademlia","https://en.wikipedia.org/wiki/Distributed_hash_table","https://github.com/libp2p/go-libp2p-kad-dht","https://en.wikipedia.org/wiki/Hash_table","https://medium.com/textileio/swapping-bits-and-distributing-hashes-on-the-decentralized-web-5da98a3507","https://www.researchgate.net/figure/2-Overlay-and-underlay-view-of-Distributed-Hash-Tables-11_fig2_304495476","https://github.com/libp2p/notes/issues/2#issuecomment-433729343","https://github.com/textileio/textile-go/projects/2","https://github.com/ipfs/research-CRDT/","https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type","https://github.com/orbitdb/ipfs-log","https://www.atlassian.com/git/tutorials/using-branches/git-merge","https://blog.thoughtram.io/git/2014/11/18/the-anatomy-of-a-git-commit.html#meet-the-tree-object","https://twitter.com/Textile01/status/1004436869734543360","https://en.wikipedia.org/wiki/Blockchain","https://medium.com/textileio","https://medium.com/textileio/enabling-the-distributed-web-abf7ab33b638","https://en.wikipedia.org/wiki/Fingerprint_%28computing%29","https://github.com/ipld/cid","https://github.com/multiformats/multihash","https://en.wikipedia.org/wiki/Cryptographic_hash_function","https://en.wikipedia.org/wiki/SHA-3","https://ipfs.io/ipfs/QmcNzGMQ1hUzSnqM2aH1Um8KWLRC4Rd9TyY4kFuYZXWXaF/Textile_Icon_500x500.png","https://github.com/textileio/textile-go","https://twitter.com/Textile01/lists/textile-team/members","https://twitter.com/Textile01","https://slack.textile.io/","https://github.com/textileio","https://www.textile.photos/#cta"],"app":"steemit/0.1","format":"html"}
parent author
parent permlinkintroduceyourself
Transaction InfoBlock #27329275/Trx fd70e3b34685167fa0c758f29486715f2170f7f3
View Raw JSON Data
{
  "op": [
    "comment",
    {
      "body": "<html>\n<p><em>Written by </em><a href=\"https://medium.com/@carsonfarmer\"><em>Carson Farmer</em></a><em> &amp; </em><a href=\"https://medium.com/@sanderpick\"><em>Sander Pick</em></a></p>\n<p><center><img src=\"https://cdn-images-1.medium.com/max/800/1*beyK889WqmNEatN55oBAnA.png\" width=\"800\" height=\"565\"/><em>Download the app, take a picture, share!</em></center></p>\n<p>Recently, &nbsp;we’ve started writing more about the technologies underlying Textile &nbsp;Photos that help keep your photos (and likes and comments, etc) safe and &nbsp;secure on the decentralized web. In our previous post, we talked about <a href=\"https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14\">the encryption process Textile Photos</a>, &nbsp;with a focus how Textile delivers end-to-end encrypted photo sharing. &nbsp;Today’s post is a follow-up (though it should also be sufficiently &nbsp;detailed to stand on its own), this time highlighting how Textile &nbsp;coordinates private photo sharing among groups of users, a feature we &nbsp;call <em>Threads</em>.</p>\n<p><strong>Why we built it</strong><br>\nWe &nbsp;designed Threads to allow groups of users to share photos securely and &nbsp;privately, without any centralized, authoritative database. We also made &nbsp;sure it all works well offline, that its possible to recover lost data, &nbsp;and that its easy to add new members.</p>\n<p><strong>What makes Threads different</strong><br>\nThreads &nbsp;allow private groups to post photos and interact over a decentralized &nbsp;network, maintaining complete control over their own content. Textile &nbsp;operates in a completely zero-knowledge framework. Private by design.</p>\n<p><strong>Why Threads are exciting</strong><br>\nBecause &nbsp;photos are just the first step. Today, Threads allow users to share a &nbsp;photo with other Thread in a secure, decentralized way. Threads can &nbsp;facilitate secure sharing, coordination, and storage of <em>many</em> types of data over a decentralized network. Upgradable by design.</p>\n<p>On &nbsp;the surface, you can think of each Thread like a decentralized &nbsp;database, shared between specific participants. We built Threads into &nbsp;the fabric of Textile (see what we did there 😉) because group members &nbsp;need a record of who shared what photo, and when. But, once we created &nbsp;Threads, we realized just how powerful a concept this was — for those &nbsp;familiar with mobile app development, think <a href=\"https://realm.io/\">Realm</a> or <a href=\"https://firebase.google.com/\">Firebase</a> but <em>without</em> the centralized server.</p>\n<p>To &nbsp;really understand what Threads brings to the table, you really need to &nbsp;understand Threads themselves. So let’s dig a bit deeper into how &nbsp;Textile conceptualizes and implements Threads, and how that helps keep &nbsp;your photos (and likes and comments, etc) safe and secure on the &nbsp;decentralized web. We’ll start by highlighting the specific requirements &nbsp;we had when developing Threads, and then break down each of these &nbsp;requirements into the specific solutions that we came up with. Along the &nbsp;way, our CTO Sander Pick will highlight how those various solutions &nbsp;came about, and why we think our approach is in the best interest of our &nbsp;users.</p>\n<h3>The experience</h3>\n<p><a href=\"https://textile.photos/\">Textile Photos</a> &nbsp;allows small, decentralized, private groups to share photos, send &nbsp;messages, and engage with each other. That’s the experience, so it has &nbsp;to ‘just work’.</p>\n<p><center><img src=\"https://cdn-images-1.medium.com/max/800/1*xEE3Pp-LdU_r8qsPvMVS8w.gif\" width=\"400\" height=\"600\"/></center></p>\n<h3>Requirements</h3>\n<p>To drive the Textile user experience, we identified five key features needed in the sharing protocols:</p>\n<ol>\n  <li><strong>A mechanism to share and receive state updates within a group of n users</strong><br>\nTo &nbsp;enable photo sharing (and other common interactions such as likes and &nbsp;comments) among a group of friends and/or colleagues, some concept of a <em>shared state</em> is required.</li>\n  <li><strong>A way to ensure the shared state stays resilient to peers dropping out or latency issues</strong><br>\nSince &nbsp;we’re operating in a mobile environment, we have to expect peers to &nbsp;continually drop ‘offline’ due to coverage issues, app back-grounding, &nbsp;battery optimizations, and a whole slew of other reasons for a mobile &nbsp;device to be cut off from a network.</li>\n  <li><strong>A way to avoid state conflicts with other members of the group</strong><br>\nOn &nbsp;top of the requirements above, when peers do come back online, we don’t &nbsp;want any state changes that were made by other members of the group &nbsp;while they were disconnected from the network to conflict with their own &nbsp;local changes.</li>\n  <li><strong>A mechanism to recover the full state from the network as a whole</strong><br>\nAnother &nbsp;important consideration in the mobile world is that the number of users &nbsp;(out of n) that are online at any given time is generally unknown, and &nbsp;quite possibly zero. To reiterate, we want a decentralized shared state, &nbsp;but it has to work <em>even when you are the only member online</em>. &nbsp;This means we have to assume the full group state may not ever be &nbsp;directly accessible (i.e., downloadable) from a single group member. &nbsp;This is in contrast to something like <a href=\"https://en.wikipedia.org/wiki/Bitcoin\">Bitcoin</a>, where new nodes are able to <a href=\"https://bitcoin.org/en/developer-guide#initial-block-download\">download the full blockchain</a> from any connected peer.</li>\n  <li><strong>Way to link updates via their content, rather than where they are stored</strong><br>\nSince we are building on top of the IPFS network, and would like to eventually support a <a href=\"https://filecoin.io/\">Filecoin</a>-based &nbsp;future in which users can select from a multitude of decentralized &nbsp;storage providers, Threads need to embrace content addressing, rather &nbsp;than location addressing. This makes it easy to grow and change the &nbsp;underlying network, without affecting data access and sharing.</li>\n</ol>\n<p>With these requirements in mind, let’s break down our solutions into their individual components…</p>\n<h3>Solutions</h3>\n<h4>1. Handling Updates — use a peer-to-peer network with structured updates</h4>\n<p>First things first: <em>how do we handle state updates between a set of distributed peers?</em> This is mostly about <a href=\"https://en.wikipedia.org/wiki/Peer-to-peer\">peer-to-peer (p2p) networking</a>. &nbsp;And when it comes to communicating between heterogeneous network &nbsp;devices (computers, phones, IoT devices, etc), we actually need many <a href=\"https://en.wikipedia.org/wiki/Lists_of_network_protocols\">different types of network protocols</a>. &nbsp;That way, no matter what type of device we are talking about — be it a &nbsp;phone, desktop computer, browser, or Internet-enabled fridge — it is &nbsp;able to communicate with other devices located in the same room, or on &nbsp;the other side of the planet.</p>\n<p>At Textile, we use the super amazing <a href=\"https://libp2p.io/\">libp2p</a> &nbsp;library for our networking needs. Libp2p is a networking stack and &nbsp;library (you might have heard it called a protocol suite) modularized &nbsp;out of the <a href=\"https://ipfs.io/\">IPFS project</a>, &nbsp;and bundled separately for other tools to use. Essentially, libp2p does &nbsp;all the heavy network lifting so that we can focus on our core task: &nbsp;exchanging updates between communicating peers.</p>\n<p><center><img src=\"https://cdn-images-1.medium.com/max/800/1*0iCOZW6HrpdQ0izG5O9ciw.png\" width=\"800\" height=\"316\"/><em>Decentralized, peer-to-peer networks are radically different types of communication networks.</em></center></p>\n<blockquote>Libp2p &nbsp;was a pretty natural choice for us. The stack includes all the crypto &nbsp;and networking protocols we need to deliver messages to group members, &nbsp;and the libp2p developer community is super responsive and excited about &nbsp;the power of p2p interactions. Easy choice.</blockquote>\n<p>The &nbsp;other really nice thing about using the libp2p library is it comes &nbsp;packed with many useful cryptography tools and functions, keeping &nbsp;communications secure. For instance, all p2p communications over the &nbsp;Textile network use the <a href=\"https://github.com/libp2p/go-libp2p-secio\">secio</a> <a href=\"https://github.com/libp2p/go-stream-security\">stream security transport</a>. &nbsp;This way, all connections use secure sessions provided by libp2p/secio &nbsp;to encrypt all traffic, whereby a TLS-like handshake is used to setup &nbsp;the initial communication channel.</p>\n<p>Like many IPFS-based projects, Textile uses <a href=\"https://developers.google.com/protocol-buffers/\">Protocol Buffers</a> &nbsp;for over-the-wire communication, and advanced cryptographic algorithms &nbsp;to secure those messages. Essentially, each update to the shared group &nbsp;state is just an encrypted Profobuf message with two parts: a header &nbsp;with author and date info, and a body with the type-specific data. These &nbsp;pieces are sent in their own inner-’envelope’ which contains a link to &nbsp;the encrypted message and the Thread ID. This inner-envelope is then &nbsp;signed by the sender and placed into the wire ‘envelope’ along with it’s &nbsp;signature. You can read more about some of the cryptographic tools &nbsp;Textile uses in <a href=\"https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14\">this previous article</a>. You can also check out how we structure our <a href=\"https://github.com/textileio/textile-go/blob/master/pb/protos/thread.proto\">Protobuf messages</a>, learn a bit more about <a href=\"https://github.com/auditdrivencrypto/secure-channel/blob/master/prior-art.md#ipfss-secure-channel\">how secio works</a>, plus check out some <a href=\"https://github.com/textileio/textile-go/commit/05a269cd5dd72da224c4d2c472abad00a078d4ca\">recent updates</a> to message encryption while you’re at it.</p>\n<h4>2. Network Resilience — support offline messaging so peers can come and&nbsp;go</h4>\n<p>If you are at all familiar with libp2p, then you might be thinking <em>“ah libp2p has a pubsub layer that would be perfect for exchanging updates to a group of connecting peers”</em>. &nbsp;And while you’d certainly be right, there are a few key limitations &nbsp;that makes using pubsub for something like Textile Photos pretty &nbsp;cumbersome. On top of this, while pubsub is super nice for things like &nbsp;chat rooms or distributed services, it is a ‘fire-and-forget’ messaging &nbsp;protocol, meaning that once a peer publishes a message, it is up to its &nbsp;peers to ensure they are listening for the right message at the right &nbsp;time. To circumvent this, some pubsub systems introduce message echoing, &nbsp;to ensure a message stays in the system long enough to be picked up by &nbsp;the peers who might need it. However, this can lead to really noisy &nbsp;network traffic, and is really just a band-aid over a larger issue.</p>\n<blockquote>Our &nbsp;initial POC involved pubsub and always-online room echoers… not &nbsp;scalable or particularly decentralized. A real solution to distributing &nbsp;state has to involve direct messaging with an offline mechanism.</blockquote>\n<p>So this starts to get at our second requirement, that <em>the shared state stays resilient to peers dropping out</em>. &nbsp;We need to assume peers might not be around to receive important &nbsp;messages in ‘real-time’, which is a common problem with p2p systems. &nbsp;Right now, Textile addresses this problem by enabling what you might &nbsp;call <em>offline messaging</em>. Since we’re <a href=\"https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14#ea20\">already using IPFS for data storage and communication</a>, &nbsp;we wanted to take advantage of some of the core technologies driving &nbsp;IPFS. In particular, we (currently) use a special fork of the <a href=\"https://en.wikipedia.org/wiki/Kademlia\">Kademlia</a>-based <a href=\"https://en.wikipedia.org/wiki/Distributed_hash_table\">distributed hash table</a> (DHT) <a href=\"https://github.com/libp2p/go-libp2p-kad-dht\">used by IPFS, </a>that allows us to post messages for a peer directly in the DHT. For those unfamiliar with DHTs, they are a <a href=\"https://en.wikipedia.org/wiki/Hash_table\">hash table</a> &nbsp;where the data is spread across a network of nodes or peers. And these &nbsp;peers are all coordinated to enable efficient access and lookup between &nbsp;nodes in a decentralized way. You can read more about this kind of stuff &nbsp;in our previous article about <a href=\"https://medium.com/textileio/swapping-bits-and-distributing-hashes-on-the-decentralized-web-5da98a3507\">how IPFS peers find, request, and retrieve content (and each other) on the decentralized web</a>. &nbsp;So, when a peer we want to communicate with is offline, rather than &nbsp;blindly sending them a message that will never be received, we post a &nbsp;message to Textile’s DHT, and they can then retrieve that message the &nbsp;next time they come online again. Conceptually simple, and works pretty &nbsp;well in practice.</p>\n<p><center><img src=\"https://cdn-images-1.medium.com/max/800/1*JsXZAveyMui2izg_-FMh0g.png\" width=\"800\" height=\"485\"/><em>p2p &nbsp;network with custom Textile DHT overlay. Peers post (key, value) &nbsp;messages (value) with a key specific to their intended recipient, and &nbsp;this key is broadcast and available to entire network; though only the &nbsp;intended recipient is able to decrypt the actual message content. Based &nbsp;on Figure 1–2 from </em><a href=\"https://www.researchgate.net/figure/2-Overlay-and-underlay-view-of-Distributed-Hash-Tables-11_fig2_304495476\"><em>this&nbsp;thesis</em></a><em>.</em></center></p>\n<p>There &nbsp;are still some issues with our current approach, including that it is &nbsp;difficult/impossible to remove messages from the DHT manually. Indeed, &nbsp;it can start to get a bit messy when left-over offline messages have to &nbsp;be retrieved each time a peer comes back online… imagine a peer that &nbsp;goes in and out of service frequently, this could lead to a lot of &nbsp;network traffic and wasted CPU cycles. So, we’ve <a href=\"https://github.com/libp2p/notes/issues/2#issuecomment-433729343\">implemented an alternative</a> &nbsp;to this DHT-based offline messaging system that does not suffer from &nbsp;these limitations (and also allows us to participate in the public IPFS &nbsp;network), while still remaining decentralized and scalable in the &nbsp;long-term. This new approach should be released soon, after more testing &nbsp;and evaluation. You can follow along with this progress as part of the <a href=\"https://github.com/textileio/textile-go/projects/2\">move towards a Cafe-based setup</a> (see also <strong>What’s Next</strong>).</p>\n<h4>3 &amp; 4. Avoiding Conflicts &amp; State Recovery —use a CRDT to keep an immutable history across&nbsp;peers</h4>\n<p>Ok, so our next requirement and its associated solution have received a <a href=\"https://github.com/ipfs/research-CRDT/\">great deal of research and development attention over the years</a>. The question of “<em>how to avoid state conflicts with other members of a group?”</em> &nbsp;comes up when working collaboratively on documents, updating shared &nbsp;databases, etc. For the purposes of updating a shared Thread of photos, &nbsp;it turns out that an <a href=\"https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type\">operation-based CRDT</a> &nbsp;that supports append-only operations is pretty much all you need to get &nbsp;going. You can think of Textile’s CRDT (which shares some ideas with <a href=\"https://github.com/orbitdb/ipfs-log\">ipfs-log</a>) &nbsp;setup as an immutable, append-only tree that can be used to model a &nbsp;mutable, shared state between peers. Every entry in the tree is saved on &nbsp;IPFS, and each points to a hash of previous entry(ies) forming a graph. &nbsp;These trees can be <a href=\"https://www.atlassian.com/git/tutorials/using-branches/git-merge\">3-way and fast-forward merged</a>.</p>\n<p>Speaking &nbsp;of forks and joins, for those familiar with git and other similar &nbsp;system, you might be thinking this sounds a lot like a <a href=\"https://blog.thoughtram.io/git/2014/11/18/the-anatomy-of-a-git-commit.html#meet-the-tree-object\">git hash tree</a>, <a href=\"https://twitter.com/Textile01/status/1004436869734543360\">Merkle DAG</a>, or even a <a href=\"https://en.wikipedia.org/wiki/Blockchain\">blockchain</a>. &nbsp;And you’d be right! The concepts are very similar, and this buys us &nbsp;some really nice properties for building and maintaining a shared state. &nbsp;By modeling our shared Thread state in this way, we benefit from tried &nbsp;and tested methods for allowing a peer to incorporate other peers’ &nbsp;updates into their state while maintaining history (via fast-forwards &nbsp;and three-way merging for example).</p>\n<blockquote>At the end of the day, a Thread is just a git-like hash tree of updates with a deterministic merge policy. Simple.</blockquote>\n<p>So &nbsp;what does this look like in practice? Currently — because things might &nbsp;change as we make improvements to the underlying implementation — each &nbsp;Thread in Textile Photos is essentially a chain of updates, where each &nbsp;update represents some specific action or event. For instance, when you &nbsp;create a new Thread, under-the-hood you are actually creating a <code>JOIN</code> update on a new Thread chain. Similarly, when you update the Thread via a new photo (<code>DATA</code> update), comment, or like (<code>ANNOTATION</code> update), you’re actually updating that Thread chain. After each modification, the <code>HEAD</code> of the Thread will point to the latest update.</p>\n<p>Building on top of these ideas, we also have concepts such as an <code>INVITE</code>, which points a new peer to a given point on the Thread chain, or a <code>MERGE</code>, which happens when the current <code>HEAD</code> &nbsp;is not contained in an incoming update’s parent list for some reason &nbsp;(maybe the peer doesn’t know about it because they were offline). If two &nbsp;peers are merging the <em>same sub trees</em>, &nbsp;all they need to do to ensure the update resolves to the same hash is &nbsp;a) include the same date b) exclude author info. To get the same date, &nbsp;they both follow a rule: choose the latest of the parents for the date &nbsp;(in practice they add a little bit extra on to keep it ahead of both &nbsp;parents).</p>\n<p>To give you a better idea of what exactly we’re talking about, consider the following set of operations: <code>User A</code> creates a new Thread, and adds a Photo. They then externally invite <code>User B</code> (sent via some other <em>secure</em> communication channel), who eventually joins the Thread. But before <code>User B</code> is able to join the Thread, <code>User A</code> adds another Photo, moving the Thread’s <code>HEAD</code> forward. By the time <code>User B</code> joins the Thread, they’d end up with a Thread sequence that looks something like this:</p>\n<p><center><img src=\"https://cdn-images-1.medium.com/max/800/1*h9eNwOauuT7mMeS4o-yGbw.png\" width=\"800\" height=\"478\"/><em>Thread &nbsp;join example. Solid arrows point towards the ‘parent’ of a given &nbsp;update, over-the-wire communications are indicated with a 📶-style &nbsp;arrow, and messages that are rebroadcast (e.g., via the welcome message) &nbsp;are indicated with a dashed arrow. Similarly, merges point to both &nbsp;their parent&nbsp;updates.</em></center></p>\n<p>Here, &nbsp;we see the merge happening at the end of the sequence because the &nbsp;bottom peer is joining via an external invite that is no longer <code>HEAD</code>&nbsp;, forcing them to merge the most recent <code>DATA</code> update with their own <code>JOIN</code> update. But since merge results are deterministic (given the same parents), both peers create the <code>MERGE</code> update locally, and do not broadcast them to avoid trading merges back and forth.</p>\n<p>A more complete sequence is given in the following figure. Suppose <code>User A</code> &nbsp;goes ‘offline’ (e.g., their phone goes to sleep, they shut down the &nbsp;app, they lose their data connection, etc), and in the mean time, both<code> Users A</code> and <code>B</code> update the Thread, with <code>User A</code> adding an <code>ANNOTATION</code> update, and <code>User B</code> adding a new Photo (<code>DATA</code> update). Now, when <code>User A</code> comes back online, there is a conflict, and both Users create a <code>MERGE</code> update to remedy this. A <code>MERGE</code> update has two parents, in this case, the <code>DATA</code> and <code>ANNOTATION</code> update from the different users. As always, the <code>HEAD</code> continues to point to the latest update (which in the example below eventually becomes an <code>ANNOTATION</code> from <code>User B</code>). Once both peers are online again, the more straightforward update and transmit mode of operation can continue.</p>\n<p><center><img src=\"https://cdn-images-1.medium.com/max/800/1*3EuEtFHqUtALTczIeZ8_eg.png\" width=\"800\" height=\"473\"/><em>More &nbsp;complex Thread interaction where one or more peers are temporarily &nbsp;offline. Note that an external invite is the same as a normal invite, &nbsp;but the invite details are encrypted with a single use key, which is &nbsp;sharable with the invite update location.</em></center></p>\n<p>The &nbsp;same properties that make hash trees or blockchains useful for &nbsp;developing a shared, consistent (consensus-driven) state, also makes it &nbsp;possible to address our fourth requirement: <em>the ability to recover the full state from the network as a whole</em>. &nbsp;Because each Thread update references its parent(s), given a single &nbsp;point on the Thread chain, we can trace back all the way to the &nbsp;beginning of the Thread. For example, at any point along the sequence in &nbsp;the above figures, a peer can trace back the history of the Thread, as &nbsp;indicated by the solid arrows. This works particularly nicely when a &nbsp;peer <code>JOIN</code>s a thread, even at a point prior to the current <code>HEAD</code>. They can simply <code>JOIN</code>, and any existing Thread member can send them the latest <code>HEAD</code> &nbsp;(even via offline messages if needed). From here, they can explore the &nbsp;entire history of the Thread with ease. This is all really similar to &nbsp;git commit speak, in which one only needs to know about a single commit &nbsp;to be able to trace back the entire history of a code project; it’s also &nbsp;essentially how blockchains work.</p>\n<h4>5. Content Addressing — store everything on IPFS and get ready to&nbsp;scale</h4>\n<p>As &nbsp;we alluded to earlier, each update to a Thread is backed by an IPFS CID &nbsp;hash (i.e., they are content addressable chunks of data on IPFS). This &nbsp;means <em>where</em> the data is stored &nbsp;is no longer relevant… IPFS will find it on the network via it’s hash. &nbsp;This helps us address our fifth requirement, that we have a <em>way to link updates via their content, rather than where they are stored</em>. We’ve covered this topic a lot <a href=\"https://medium.com/textileio\">in the past</a>, but for the uninitiated, the next paragraph provides a summary of how content addressing on IPFS works (pulled from <a href=\"https://medium.com/textileio/enabling-the-distributed-web-abf7ab33b638\">this previous article</a>).</p>\n<p>Rather than referencing a file or chunk of data by its location (think HTTP), we reference it via its <a href=\"https://en.wikipedia.org/wiki/Fingerprint_%28computing%29\">fingerprint</a>. In IPFS and other such systems, this means identifying content by its cryptographic hash, or even better, a <a href=\"https://github.com/ipld/cid\"><em>self-describing </em>content-addressed identifier</a> (<a href=\"https://github.com/multiformats/multihash\">multihash</a>). A cryptographic hash is a (relatively) short alphanumeric string that’s calculated by running your content through a <a href=\"https://en.wikipedia.org/wiki/Cryptographic_hash_function\">cryptographic hash function</a> (like <a href=\"https://en.wikipedia.org/wiki/SHA-3\">SHA</a>). For example, when the (unencrypted) <a href=\"https://ipfs.io/ipfs/QmcNzGMQ1hUzSnqM2aH1Um8KWLRC4Rd9TyY4kFuYZXWXaF/Textile_Icon_500x500.png\">Textile logo</a> is added to IPFS, its multihash ends up being <code>QmbgGgWW3vH7v9FDxVCzcouKGChqGEjtf6YLDUgSHnk5J2</code>. This ‘hash’ is actually the CID (Content IDentifier) for that file, computed from the <em>raw data </em>within that PNG. It is guaranteed to be cryptographically unique to the contents of <em>that </em>file, and that file only. If we change that file by even one bit, the hash will become something completely different.</p>\n<p>Now, &nbsp;when we want to access a file over IPFS (like the above logo), we can &nbsp;simply ask the IPFS network for the file with that exact CID, the &nbsp;network will find the peers that have the data (using a DHT), retrieve &nbsp;it, and verify (using the CID) that it’s the correct file. What this &nbsp;means is we can technically get the file from <em>multiple </em>places &nbsp;because as long as the file matches the hash, we know we’re getting the &nbsp;right data. Which brings us to the solution to our final requirement… &nbsp;use IPFS! For now, Textile is maintaining a network of large, &nbsp;homogeneous, volunteer nodes (we call them <a href=\"https://github.com/textileio/textile-go\">Cafe</a>s) &nbsp;to ‘pin’ and store content on IPFS. It is important to note here that &nbsp;the other nodes doing the pinning are the same as the nodes on your &nbsp;phone — Textile Nodes that offer a pinning service to other peers. Soon, &nbsp;we’ll allow users to elect their own Cafe nodes, add even add &nbsp;additional nodes for redundancy. All this could eventually be driven by &nbsp;Filecoin for even greater scalablility and flexibility.</p>\n<h3>What’s Next?</h3>\n<p>So &nbsp;there you have it. Five solutions to five requirements for seamless, &nbsp;secure, decentralized photo sharing and backup. Easy 😉. And at a &nbsp;conceptual level, the Textile Thread protocol <em>is</em> &nbsp;relatively simple: blocks of operations chained together to produce a &nbsp;beautiful Thread of photos. But there’s a lot of complexity going on &nbsp;under-the-hood that has required a lot of experimentation, testing, and &nbsp;limit pushing, especially on mobile. And our journey isn’t over yet.</p>\n<p><a href=\"https://twitter.com/Textile01/lists/textile-team/members\">The Textile team</a> &nbsp;is still hard at work iterating, updating, and improving upon what we &nbsp;already have working. For example, we’ll soon to moving to a new offline &nbsp;messaging system that allow us to drop the custom DHT fork, and move &nbsp;back to the public IPFS network. On top of this, our <a href=\"https://github.com/textileio/textile-go/projects/2\">move to more powerful backup</a> &nbsp;and recovery capabilities has us taking new approaches to security, &nbsp;profile management, offline interactions, and much much more. On top of &nbsp;these changes, the team is actively working to modularize the Threads &nbsp;concept and code into its own stand-alone package, which should provide &nbsp;developers with something akin to a Realm and/or Firebase layer for &nbsp;decentralized mobile applications!</p>\n<p>If you are interested in learning more about this stuff, reach out over <a href=\"https://twitter.com/Textile01\">Twitter</a> or <a href=\"https://slack.textile.io/\">Slack</a>, &nbsp;or pull us aside the next time you see us at a conference or event. We’re happy to provide background, thoughts, and opinions on how we &nbsp;think the future of decentralized apps will play out. In the mean time, &nbsp;don’t forget to check out our <a href=\"https://github.com/textileio\">GitHub repos</a> &nbsp;for code and PRs that showcase our current and old implementations. We &nbsp;try to make sure all our development happens out in the open, so you can &nbsp;see things as they develop. Additionally, if you haven’t already, <a href=\"https://www.textile.photos/#cta\">don’t miss out on signing up for our waitlist</a>, where you can get early access to Textile Photos, the beautiful interface to Textile’s Threads.</p>\n</html>",
      "title": "Hi, Steemit! We're Textile. Here's a deeper look at the tech behind our Threads protocol.",
      "author": "sanderpick",
      "permlink": "hi-steemit-we-re-textile-here-s-a-deeper-look-at-the-tech-behind-our-threads-protocol",
      "json_metadata": "{\"tags\":[\"introduceyourself\",\"ipfs\",\"libp2p\",\"filecoin\",\"mobile\"],\"image\":[\"https://cdn-images-1.medium.com/max/800/1*beyK889WqmNEatN55oBAnA.png\",\"https://cdn-images-1.medium.com/max/800/1*xEE3Pp-LdU_r8qsPvMVS8w.gif\",\"https://cdn-images-1.medium.com/max/800/1*0iCOZW6HrpdQ0izG5O9ciw.png\",\"https://cdn-images-1.medium.com/max/800/1*JsXZAveyMui2izg_-FMh0g.png\",\"https://cdn-images-1.medium.com/max/800/1*h9eNwOauuT7mMeS4o-yGbw.png\",\"https://cdn-images-1.medium.com/max/800/1*3EuEtFHqUtALTczIeZ8_eg.png\"],\"links\":[\"https://medium.com/@carsonfarmer\",\"https://medium.com/@sanderpick\",\"https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14\",\"https://realm.io/\",\"https://firebase.google.com/\",\"https://textile.photos/\",\"https://en.wikipedia.org/wiki/Bitcoin\",\"https://bitcoin.org/en/developer-guide#initial-block-download\",\"https://filecoin.io/\",\"https://en.wikipedia.org/wiki/Peer-to-peer\",\"https://en.wikipedia.org/wiki/Lists_of_network_protocols\",\"https://libp2p.io/\",\"https://ipfs.io/\",\"https://github.com/libp2p/go-libp2p-secio\",\"https://github.com/libp2p/go-stream-security\",\"https://developers.google.com/protocol-buffers/\",\"https://github.com/textileio/textile-go/blob/master/pb/protos/thread.proto\",\"https://github.com/auditdrivencrypto/secure-channel/blob/master/prior-art.md#ipfss-secure-channel\",\"https://github.com/textileio/textile-go/commit/05a269cd5dd72da224c4d2c472abad00a078d4ca\",\"https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14#ea20\",\"https://en.wikipedia.org/wiki/Kademlia\",\"https://en.wikipedia.org/wiki/Distributed_hash_table\",\"https://github.com/libp2p/go-libp2p-kad-dht\",\"https://en.wikipedia.org/wiki/Hash_table\",\"https://medium.com/textileio/swapping-bits-and-distributing-hashes-on-the-decentralized-web-5da98a3507\",\"https://www.researchgate.net/figure/2-Overlay-and-underlay-view-of-Distributed-Hash-Tables-11_fig2_304495476\",\"https://github.com/libp2p/notes/issues/2#issuecomment-433729343\",\"https://github.com/textileio/textile-go/projects/2\",\"https://github.com/ipfs/research-CRDT/\",\"https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type\",\"https://github.com/orbitdb/ipfs-log\",\"https://www.atlassian.com/git/tutorials/using-branches/git-merge\",\"https://blog.thoughtram.io/git/2014/11/18/the-anatomy-of-a-git-commit.html#meet-the-tree-object\",\"https://twitter.com/Textile01/status/1004436869734543360\",\"https://en.wikipedia.org/wiki/Blockchain\",\"https://medium.com/textileio\",\"https://medium.com/textileio/enabling-the-distributed-web-abf7ab33b638\",\"https://en.wikipedia.org/wiki/Fingerprint_%28computing%29\",\"https://github.com/ipld/cid\",\"https://github.com/multiformats/multihash\",\"https://en.wikipedia.org/wiki/Cryptographic_hash_function\",\"https://en.wikipedia.org/wiki/SHA-3\",\"https://ipfs.io/ipfs/QmcNzGMQ1hUzSnqM2aH1Um8KWLRC4Rd9TyY4kFuYZXWXaF/Textile_Icon_500x500.png\",\"https://github.com/textileio/textile-go\",\"https://twitter.com/Textile01/lists/textile-team/members\",\"https://twitter.com/Textile01\",\"https://slack.textile.io/\",\"https://github.com/textileio\",\"https://www.textile.photos/#cta\"],\"app\":\"steemit/0.1\",\"format\":\"html\"}",
      "parent_author": "",
      "parent_permlink": "introduceyourself"
    }
  ],
  "block": 27329275,
  "trx_id": "fd70e3b34685167fa0c758f29486715f2170f7f3",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T21:33:21",
  "virtual_op": false,
  "trx_in_block": 11
}
blocktradesblockchain operation: transfer to vesting completed
2018/11/01 21:29:24
to accountsanderpick
hive vested6.003 HIVE
from accountblocktrades
vesting shares received12106.267835 VESTS
Transaction InfoBlock #27329196/Trx 9b95c5fec4a2bc49f4c7902bd3b1329bfda62d6b
View Raw JSON Data
{
  "op": [
    "transfer_to_vesting_completed",
    {
      "to_account": "sanderpick",
      "hive_vested": "6.003 HIVE",
      "from_account": "blocktrades",
      "vesting_shares_received": "12106.267835 VESTS"
    }
  ],
  "block": 27329196,
  "trx_id": "9b95c5fec4a2bc49f4c7902bd3b1329bfda62d6b",
  "op_in_trx": 1,
  "timestamp": "2018-11-01T21:29:24",
  "virtual_op": true,
  "trx_in_block": 23
}
blocktradespowered up 6.003 HIVE to @sanderpick
2018/11/01 21:29:24
tosanderpick
fromblocktrades
amount6.003 HIVE
Transaction InfoBlock #27329196/Trx 9b95c5fec4a2bc49f4c7902bd3b1329bfda62d6b
View Raw JSON Data
{
  "op": [
    "transfer_to_vesting",
    {
      "to": "sanderpick",
      "from": "blocktrades",
      "amount": "6.003 HIVE"
    }
  ],
  "block": 27329196,
  "trx_id": "9b95c5fec4a2bc49f4c7902bd3b1329bfda62d6b",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T21:29:24",
  "virtual_op": false,
  "trx_in_block": 23
}
blocktradesblockchain operation: transfer to vesting completed
2018/11/01 20:54:03
to accountsanderpick
hive vested5.885 HIVE
from accountblocktrades
vesting shares received11868.312524 VESTS
Transaction InfoBlock #27328489/Trx 1500dddac4e02593504a0d402b441c25fcb7fbde
View Raw JSON Data
{
  "op": [
    "transfer_to_vesting_completed",
    {
      "to_account": "sanderpick",
      "hive_vested": "5.885 HIVE",
      "from_account": "blocktrades",
      "vesting_shares_received": "11868.312524 VESTS"
    }
  ],
  "block": 27328489,
  "trx_id": "1500dddac4e02593504a0d402b441c25fcb7fbde",
  "op_in_trx": 1,
  "timestamp": "2018-11-01T20:54:03",
  "virtual_op": true,
  "trx_in_block": 5
}
blocktradespowered up 5.885 HIVE to @sanderpick
2018/11/01 20:54:03
tosanderpick
fromblocktrades
amount5.885 HIVE
Transaction InfoBlock #27328489/Trx 1500dddac4e02593504a0d402b441c25fcb7fbde
View Raw JSON Data
{
  "op": [
    "transfer_to_vesting",
    {
      "to": "sanderpick",
      "from": "blocktrades",
      "amount": "5.885 HIVE"
    }
  ],
  "block": 27328489,
  "trx_id": "1500dddac4e02593504a0d402b441c25fcb7fbde",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T20:54:03",
  "virtual_op": false,
  "trx_in_block": 5
}
2018/11/01 16:45:27
voteralphabot
authorsanderpick
weight950 (9.50%)
rshares31247304
permlinktextile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol
pending payout0.000 HBD
total vote weight23271
Transaction InfoBlock #27323518/Trx b454fda4b9b3fe49a37f5746c6113227be91ecbd
View Raw JSON Data
{
  "op": [
    "effective_comment_vote",
    {
      "voter": "alphabot",
      "author": "sanderpick",
      "weight": 950,
      "rshares": 31247304,
      "permlink": "textile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol",
      "pending_payout": "0.000 HBD",
      "total_vote_weight": 23271
    }
  ],
  "block": 27323518,
  "trx_id": "b454fda4b9b3fe49a37f5746c6113227be91ecbd",
  "op_in_trx": 1,
  "timestamp": "2018-11-01T16:45:27",
  "virtual_op": true,
  "trx_in_block": 8
}
2018/11/01 16:45:27
voteralphabot
authorsanderpick
weight100 (1.00%)
permlinktextile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol
Transaction InfoBlock #27323518/Trx b454fda4b9b3fe49a37f5746c6113227be91ecbd
View Raw JSON Data
{
  "op": [
    "vote",
    {
      "voter": "alphabot",
      "author": "sanderpick",
      "weight": 100,
      "permlink": "textile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol"
    }
  ],
  "block": 27323518,
  "trx_id": "b454fda4b9b3fe49a37f5746c6113227be91ecbd",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T16:45:27",
  "virtual_op": false,
  "trx_in_block": 8
}
2018/11/01 16:45:15
body[Empty]
title[Deleted]
authorsanderpick
permlinktextile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol
json metadata{"tags":["mobile"],"app":"steemit/0.1","format":"markdown"}
parent author
parent permlinkmobile
Transaction InfoBlock #27323514/Trx 5ccb9f14d684c94581aaef41e1e357c1dff83b1b
View Raw JSON Data
{
  "op": [
    "comment",
    {
      "body": "[Empty]",
      "title": "[Deleted]",
      "author": "sanderpick",
      "permlink": "textile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol",
      "json_metadata": "{\"tags\":[\"mobile\"],\"app\":\"steemit/0.1\",\"format\":\"markdown\"}",
      "parent_author": "",
      "parent_permlink": "mobile"
    }
  ],
  "block": 27323514,
  "trx_id": "5ccb9f14d684c94581aaef41e1e357c1dff83b1b",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T16:45:15",
  "virtual_op": false,
  "trx_in_block": 12
}
2018/11/01 16:37:45
voterfastresteem
authorsanderpick
weight448 (4.48%)
rshares30423177
permlinktextile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol
pending payout0.000 HBD
total vote weight22317
Transaction InfoBlock #27323364/Trx 52ead77f557e4010ea4f3345ed1058e84313af01
View Raw JSON Data
{
  "op": [
    "effective_comment_vote",
    {
      "voter": "fastresteem",
      "author": "sanderpick",
      "weight": 448,
      "rshares": 30423177,
      "permlink": "textile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol",
      "pending_payout": "0.000 HBD",
      "total_vote_weight": 22317
    }
  ],
  "block": 27323364,
  "trx_id": "52ead77f557e4010ea4f3345ed1058e84313af01",
  "op_in_trx": 1,
  "timestamp": "2018-11-01T16:37:45",
  "virtual_op": true,
  "trx_in_block": 31
}
2018/11/01 16:37:45
voterfastresteem
authorsanderpick
weight100 (1.00%)
permlinktextile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol
Transaction InfoBlock #27323364/Trx 52ead77f557e4010ea4f3345ed1058e84313af01
View Raw JSON Data
{
  "op": [
    "vote",
    {
      "voter": "fastresteem",
      "author": "sanderpick",
      "weight": 100,
      "permlink": "textile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol"
    }
  ],
  "block": 27323364,
  "trx_id": "52ead77f557e4010ea4f3345ed1058e84313af01",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T16:37:45",
  "virtual_op": false,
  "trx_in_block": 31
}
2018/11/01 16:37:36
body<html> <p><em>Written by </em><a href="https://medium.com/@carsonfarmer"><em>Carson Farmer</em></a><em> &amp; </em><a href="https://medium.com/@sanderpick"><em>Sander Pick</em></a></p> <p><center><img src="https://cdn-images-1.medium.com/max/800/1*beyK889WqmNEatN55oBAnA.png" width="800" height="565"/><em>Download the app, take a picture, share!</em></center></p> <p>Recently, &nbsp;we’ve started writing more about the technologies underlying Textile &nbsp;Photos that help keep your photos (and likes and comments, etc) safe and &nbsp;secure on the decentralized web. In our previous post, we talked about <a href="https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14">the encryption process Textile Photos</a>, &nbsp;with a focus how Textile delivers end-to-end encrypted photo sharing. &nbsp;Today’s post is a follow-up (though it should also be sufficiently &nbsp;detailed to stand on its own), this time highlighting how Textile &nbsp;coordinates private photo sharing among groups of users, a feature we &nbsp;call <em>Threads</em>.</p> <p><strong>Why we built it</strong><br> We &nbsp;designed Threads to allow groups of users to share photos securely and &nbsp;privately, without any centralized, authoritative database. We also made &nbsp;sure it all works well offline, that its possible to recover lost data, &nbsp;and that its easy to add new members.</p> <p><strong>What makes Threads different</strong><br> Threads &nbsp;allow private groups to post photos and interact over a decentralized &nbsp;network, maintaining complete control over their own content. Textile &nbsp;operates in a completely zero-knowledge framework. Private by design.</p> <p><strong>Why Threads are exciting</strong><br> Because &nbsp;photos are just the first step. Today, Threads allow users to share a &nbsp;photo with other Thread in a secure, decentralized way. Threads can &nbsp;facilitate secure sharing, coordination, and storage of <em>many</em> types of data over a decentralized network. Upgradable by design.</p> <p>On &nbsp;the surface, you can think of each Thread like a decentralized &nbsp;database, shared between specific participants. We built Threads into &nbsp;the fabric of Textile (see what we did there 😉) because group members &nbsp;need a record of who shared what photo, and when. But, once we created &nbsp;Threads, we realized just how powerful a concept this was — for those &nbsp;familiar with mobile app development, think <a href="https://realm.io/">Realm</a> or <a href="https://firebase.google.com/">Firebase</a> but <em>without</em> the centralized server.</p> <p>To &nbsp;really understand what Threads brings to the table, you really need to &nbsp;understand Threads themselves. So let’s dig a bit deeper into how &nbsp;Textile conceptualizes and implements Threads, and how that helps keep &nbsp;your photos (and likes and comments, etc) safe and secure on the &nbsp;decentralized web. We’ll start by highlighting the specific requirements &nbsp;we had when developing Threads, and then break down each of these &nbsp;requirements into the specific solutions that we came up with. Along the &nbsp;way, our CTO Sander Pick will highlight how those various solutions &nbsp;came about, and why we think our approach is in the best interest of our &nbsp;users.</p> <h3>The experience</h3> <p><a href="https://textile.photos/">Textile Photos</a> &nbsp;allows small, decentralized, private groups to share photos, send &nbsp;messages, and engage with each other. That’s the experience, so it has &nbsp;to ‘just work’.</p> <p><center><img src="https://cdn-images-1.medium.com/max/800/1*xEE3Pp-LdU_r8qsPvMVS8w.gif" width="400" height="600"/></center></p> <h3>Requirements</h3> <p>To drive the Textile user experience, we identified five key features needed in the sharing protocols:</p> <ol> <li><strong>A mechanism to share and receive state updates within a group of n users</strong><br> To &nbsp;enable photo sharing (and other common interactions such as likes and &nbsp;comments) among a group of friends and/or colleagues, some concept of a <em>shared state</em> is required.</li> <li><strong>A way to ensure the shared state stays resilient to peers dropping out or latency issues</strong><br> Since &nbsp;we’re operating in a mobile environment, we have to expect peers to &nbsp;continually drop ‘offline’ due to coverage issues, app back-grounding, &nbsp;battery optimizations, and a whole slew of other reasons for a mobile &nbsp;device to be cut off from a network.</li> <li><strong>A way to avoid state conflicts with other members of the group</strong><br> On &nbsp;top of the requirements above, when peers do come back online, we don’t &nbsp;want any state changes that were made by other members of the group &nbsp;while they were disconnected from the network to conflict with their own &nbsp;local changes.</li> <li><strong>A mechanism to recover the full state from the network as a whole</strong><br> Another &nbsp;important consideration in the mobile world is that the number of users &nbsp;(out of n) that are online at any given time is generally unknown, and &nbsp;quite possibly zero. To reiterate, we want a decentralized shared state, &nbsp;but it has to work <em>even when you are the only member online</em>. &nbsp;This means we have to assume the full group state may not ever be &nbsp;directly accessible (i.e., downloadable) from a single group member. &nbsp;This is in contrast to something like <a href="https://en.wikipedia.org/wiki/Bitcoin">Bitcoin</a>, where new nodes are able to <a href="https://bitcoin.org/en/developer-guide#initial-block-download">download the full blockchain</a> from any connected peer.</li> <li><strong>Way to link updates via their content, rather than where they are stored</strong><br> Since we are building on top of the IPFS network, and would like to eventually support a <a href="https://filecoin.io/">Filecoin</a>-based &nbsp;future in which users can select from a multitude of decentralized &nbsp;storage providers, Threads need to embrace content addressing, rather &nbsp;than location addressing. This makes it easy to grow and change the &nbsp;underlying network, without affecting data access and sharing.</li> </ol> <p>With these requirements in mind, let’s break down our solutions into their individual components…</p> <h3>Solutions</h3> <h4>1. Handling Updates — use a peer-to-peer network with structured updates</h4> <p>First things first: <em>how do we handle state updates between a set of distributed peers?</em> This is mostly about <a href="https://en.wikipedia.org/wiki/Peer-to-peer">peer-to-peer (p2p) networking</a>. &nbsp;And when it comes to communicating between heterogeneous network &nbsp;devices (computers, phones, IoT devices, etc), we actually need many <a href="https://en.wikipedia.org/wiki/Lists_of_network_protocols">different types of network protocols</a>. &nbsp;That way, no matter what type of device we are talking about — be it a &nbsp;phone, desktop computer, browser, or Internet-enabled fridge — it is &nbsp;able to communicate with other devices located in the same room, or on &nbsp;the other side of the planet.</p> <p>At Textile, we use the super amazing <a href="https://libp2p.io/">libp2p</a> &nbsp;library for our networking needs. Libp2p is a networking stack and &nbsp;library (you might have heard it called a protocol suite) modularized &nbsp;out of the <a href="https://ipfs.io/">IPFS project</a>, &nbsp;and bundled separately for other tools to use. Essentially, libp2p does &nbsp;all the heavy network lifting so that we can focus on our core task: &nbsp;exchanging updates between communicating peers.</p> <p><center><img src="https://cdn-images-1.medium.com/max/800/1*0iCOZW6HrpdQ0izG5O9ciw.png" width="800" height="316"/><em>Decentralized, peer-to-peer networks are radically different types of communication networks.</em></center></p> <blockquote>Libp2p &nbsp;was a pretty natural choice for us. The stack includes all the crypto &nbsp;and networking protocols we need to deliver messages to group members, &nbsp;and the libp2p developer community is super responsive and excited about &nbsp;the power of p2p interactions. Easy choice.</blockquote> <p>The &nbsp;other really nice thing about using the libp2p library is it comes &nbsp;packed with many useful cryptography tools and functions, keeping &nbsp;communications secure. For instance, all p2p communications over the &nbsp;Textile network use the <a href="https://github.com/libp2p/go-libp2p-secio">secio</a> <a href="https://github.com/libp2p/go-stream-security">stream security transport</a>. &nbsp;This way, all connections use secure sessions provided by libp2p/secio &nbsp;to encrypt all traffic, whereby a TLS-like handshake is used to setup &nbsp;the initial communication channel.</p> <p>Like many IPFS-based projects, Textile uses <a href="https://developers.google.com/protocol-buffers/">Protocol Buffers</a> &nbsp;for over-the-wire communication, and advanced cryptographic algorithms &nbsp;to secure those messages. Essentially, each update to the shared group &nbsp;state is just an encrypted Profobuf message with two parts: a header &nbsp;with author and date info, and a body with the type-specific data. These &nbsp;pieces are sent in their own inner-’envelope’ which contains a link to &nbsp;the encrypted message and the Thread ID. This inner-envelope is then &nbsp;signed by the sender and placed into the wire ‘envelope’ along with it’s &nbsp;signature. You can read more about some of the cryptographic tools &nbsp;Textile uses in <a href="https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14">this previous article</a>. You can also check out how we structure our <a href="https://github.com/textileio/textile-go/blob/master/pb/protos/thread.proto">Protobuf messages</a>, learn a bit more about <a href="https://github.com/auditdrivencrypto/secure-channel/blob/master/prior-art.md#ipfss-secure-channel">how secio works</a>, plus check out some <a href="https://github.com/textileio/textile-go/commit/05a269cd5dd72da224c4d2c472abad00a078d4ca">recent updates</a> to message encryption while you’re at it.</p> <h4>2. Network Resilience — support offline messaging so peers can come and&nbsp;go</h4> <p>If you are at all familiar with libp2p, then you might be thinking <em>“ah libp2p has a pubsub layer that would be perfect for exchanging updates to a group of connecting peers”</em>. &nbsp;And while you’d certainly be right, there are a few key limitations &nbsp;that makes using pubsub for something like Textile Photos pretty &nbsp;cumbersome. On top of this, while pubsub is super nice for things like &nbsp;chat rooms or distributed services, it is a ‘fire-and-forget’ messaging &nbsp;protocol, meaning that once a peer publishes a message, it is up to its &nbsp;peers to ensure they are listening for the right message at the right &nbsp;time. To circumvent this, some pubsub systems introduce message echoing, &nbsp;to ensure a message stays in the system long enough to be picked up by &nbsp;the peers who might need it. However, this can lead to really noisy &nbsp;network traffic, and is really just a band-aid over a larger issue.</p> <blockquote>Our &nbsp;initial POC involved pubsub and always-online room echoers… not &nbsp;scalable or particularly decentralized. A real solution to distributing &nbsp;state has to involve direct messaging with an offline mechanism.</blockquote> <p>So this starts to get at our second requirement, that <em>the shared state stays resilient to peers dropping out</em>. &nbsp;We need to assume peers might not be around to receive important &nbsp;messages in ‘real-time’, which is a common problem with p2p systems. &nbsp;Right now, Textile addresses this problem by enabling what you might &nbsp;call <em>offline messaging</em>. Since we’re <a href="https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14#ea20">already using IPFS for data storage and communication</a>, &nbsp;we wanted to take advantage of some of the core technologies driving &nbsp;IPFS. In particular, we (currently) use a special fork of the <a href="https://en.wikipedia.org/wiki/Kademlia">Kademlia</a>-based <a href="https://en.wikipedia.org/wiki/Distributed_hash_table">distributed hash table</a> (DHT) <a href="https://github.com/libp2p/go-libp2p-kad-dht">used by IPFS, </a>that allows us to post messages for a peer directly in the DHT. For those unfamiliar with DHTs, they are a <a href="https://en.wikipedia.org/wiki/Hash_table">hash table</a> &nbsp;where the data is spread across a network of nodes or peers. And these &nbsp;peers are all coordinated to enable efficient access and lookup between &nbsp;nodes in a decentralized way. You can read more about this kind of stuff &nbsp;in our previous article about <a href="https://medium.com/textileio/swapping-bits-and-distributing-hashes-on-the-decentralized-web-5da98a3507">how IPFS peers find, request, and retrieve content (and each other) on the decentralized web</a>. &nbsp;So, when a peer we want to communicate with is offline, rather than &nbsp;blindly sending them a message that will never be received, we post a &nbsp;message to Textile’s DHT, and they can then retrieve that message the &nbsp;next time they come online again. Conceptually simple, and works pretty &nbsp;well in practice.</p> <p><center><img src="https://cdn-images-1.medium.com/max/800/1*JsXZAveyMui2izg_-FMh0g.png" width="800" height="485"/><em>p2p &nbsp;network with custom Textile DHT overlay. Peers post (key, value) &nbsp;messages (value) with a key specific to their intended recipient, and &nbsp;this key is broadcast and available to entire network; though only the &nbsp;intended recipient is able to decrypt the actual message content. Based &nbsp;on Figure 1–2 from </em><a href="https://www.researchgate.net/figure/2-Overlay-and-underlay-view-of-Distributed-Hash-Tables-11_fig2_304495476"><em>this&nbsp;thesis</em></a><em>.</em></center></p> <p>There &nbsp;are still some issues with our current approach, including that it is &nbsp;difficult/impossible to remove messages from the DHT manually. Indeed, &nbsp;it can start to get a bit messy when left-over offline messages have to &nbsp;be retrieved each time a peer comes back online… imagine a peer that &nbsp;goes in and out of service frequently, this could lead to a lot of &nbsp;network traffic and wasted CPU cycles. So, we’ve <a href="https://github.com/libp2p/notes/issues/2#issuecomment-433729343">implemented an alternative</a> &nbsp;to this DHT-based offline messaging system that does not suffer from &nbsp;these limitations (and also allows us to participate in the public IPFS &nbsp;network), while still remaining decentralized and scalable in the &nbsp;long-term. This new approach should be released soon, after more testing &nbsp;and evaluation. You can follow along with this progress as part of the <a href="https://github.com/textileio/textile-go/projects/2">move towards a Cafe-based setup</a> (see also <strong>What’s Next</strong>).</p> <h4>3 &amp; 4. Avoiding Conflicts &amp; State Recovery —use a CRDT to keep an immutable history across&nbsp;peers</h4> <p>Ok, so our next requirement and its associated solution have received a <a href="https://github.com/ipfs/research-CRDT/">great deal of research and development attention over the years</a>. The question of “<em>how to avoid state conflicts with other members of a group?”</em> &nbsp;comes up when working collaboratively on documents, updating shared &nbsp;databases, etc. For the purposes of updating a shared Thread of photos, &nbsp;it turns out that an <a href="https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type">operation-based CRDT</a> &nbsp;that supports append-only operations is pretty much all you need to get &nbsp;going. You can think of Textile’s CRDT (which shares some ideas with <a href="https://github.com/orbitdb/ipfs-log">ipfs-log</a>) &nbsp;setup as an immutable, append-only tree that can be used to model a &nbsp;mutable, shared state between peers. Every entry in the tree is saved on &nbsp;IPFS, and each points to a hash of previous entry(ies) forming a graph. &nbsp;These trees can be <a href="https://www.atlassian.com/git/tutorials/using-branches/git-merge">3-way and fast-forward merged</a>.</p> <p>Speaking &nbsp;of forks and joins, for those familiar with git and other similar &nbsp;system, you might be thinking this sounds a lot like a <a href="https://blog.thoughtram.io/git/2014/11/18/the-anatomy-of-a-git-commit.html#meet-the-tree-object">git hash tree</a>, <a href="https://twitter.com/Textile01/status/1004436869734543360">Merkle DAG</a>, or even a <a href="https://en.wikipedia.org/wiki/Blockchain">blockchain</a>. &nbsp;And you’d be right! The concepts are very similar, and this buys us &nbsp;some really nice properties for building and maintaining a shared state. &nbsp;By modeling our shared Thread state in this way, we benefit from tried &nbsp;and tested methods for allowing a peer to incorporate other peers’ &nbsp;updates into their state while maintaining history (via fast-forwards &nbsp;and three-way merging for example).</p> <blockquote>At the end of the day, a Thread is just a git-like hash tree of updates with a deterministic merge policy. Simple.</blockquote> <p>So &nbsp;what does this look like in practice? Currently — because things might &nbsp;change as we make improvements to the underlying implementation — each &nbsp;Thread in Textile Photos is essentially a chain of updates, where each &nbsp;update represents some specific action or event. For instance, when you &nbsp;create a new Thread, under-the-hood you are actually creating a <code>JOIN</code> update on a new Thread chain. Similarly, when you update the Thread via a new photo (<code>DATA</code> update), comment, or like (<code>ANNOTATION</code> update), you’re actually updating that Thread chain. After each modification, the <code>HEAD</code> of the Thread will point to the latest update.</p> <p>Building on top of these ideas, we also have concepts such as an <code>INVITE</code>, which points a new peer to a given point on the Thread chain, or a <code>MERGE</code>, which happens when the current <code>HEAD</code> &nbsp;is not contained in an incoming update’s parent list for some reason &nbsp;(maybe the peer doesn’t know about it because they were offline). If two &nbsp;peers are merging the <em>same sub trees</em>, &nbsp;all they need to do to ensure the update resolves to the same hash is &nbsp;a) include the same date b) exclude author info. To get the same date, &nbsp;they both follow a rule: choose the latest of the parents for the date &nbsp;(in practice they add a little bit extra on to keep it ahead of both &nbsp;parents).</p> <p>To give you a better idea of what exactly we’re talking about, consider the following set of operations: <code>User A</code> creates a new Thread, and adds a Photo. They then externally invite <code>User B</code> (sent via some other <em>secure</em> communication channel), who eventually joins the Thread. But before <code>User B</code> is able to join the Thread, <code>User A</code> adds another Photo, moving the Thread’s <code>HEAD</code> forward. By the time <code>User B</code> joins the Thread, they’d end up with a Thread sequence that looks something like this:</p> <p><center><img src="https://cdn-images-1.medium.com/max/800/1*h9eNwOauuT7mMeS4o-yGbw.png" width="800" height="478"/><em>Thread &nbsp;join example. Solid arrows point towards the ‘parent’ of a given &nbsp;update, over-the-wire communications are indicated with a 📶-style &nbsp;arrow, and messages that are rebroadcast (e.g., via the welcome message) &nbsp;are indicated with a dashed arrow. Similarly, merges point to both &nbsp;their parent&nbsp;updates.</em></center></p> <p>Here, &nbsp;we see the merge happening at the end of the sequence because the &nbsp;bottom peer is joining via an external invite that is no longer <code>HEAD</code>&nbsp;, forcing them to merge the most recent <code>DATA</code> update with their own <code>JOIN</code> update. But since merge results are deterministic (given the same parents), both peers create the <code>MERGE</code> update locally, and do not broadcast them to avoid trading merges back and forth.</p> <p>A more complete sequence is given in the following figure. Suppose <code>User A</code> &nbsp;goes ‘offline’ (e.g., their phone goes to sleep, they shut down the &nbsp;app, they lose their data connection, etc), and in the mean time, both<code> Users A</code> and <code>B</code> update the Thread, with <code>User A</code> adding an <code>ANNOTATION</code> update, and <code>User B</code> adding a new Photo (<code>DATA</code> update). Now, when <code>User A</code> comes back online, there is a conflict, and both Users create a <code>MERGE</code> update to remedy this. A <code>MERGE</code> update has two parents, in this case, the <code>DATA</code> and <code>ANNOTATION</code> update from the different users. As always, the <code>HEAD</code> continues to point to the latest update (which in the example below eventually becomes an <code>ANNOTATION</code> from <code>User B</code>). Once both peers are online again, the more straightforward update and transmit mode of operation can continue.</p> <p><center><img src="https://cdn-images-1.medium.com/max/800/1*3EuEtFHqUtALTczIeZ8_eg.png" width="800" height="473"/><em>More &nbsp;complex Thread interaction where one or more peers are temporarily &nbsp;offline. Note that an external invite is the same as a normal invite, &nbsp;but the invite details are encrypted with a single use key, which is &nbsp;sharable with the invite update location.</em></center></p> <p>The &nbsp;same properties that make hash trees or blockchains useful for &nbsp;developing a shared, consistent (consensus-driven) state, also makes it &nbsp;possible to address our fourth requirement: <em>the ability to recover the full state from the network as a whole</em>. &nbsp;Because each Thread update references its parent(s), given a single &nbsp;point on the Thread chain, we can trace back all the way to the &nbsp;beginning of the Thread. For example, at any point along the sequence in &nbsp;the above figures, a peer can trace back the history of the Thread, as &nbsp;indicated by the solid arrows. This works particularly nicely when a &nbsp;peer <code>JOIN</code>s a thread, even at a point prior to the current <code>HEAD</code>. They can simply <code>JOIN</code>, and any existing Thread member can send them the latest <code>HEAD</code> &nbsp;(even via offline messages if needed). From here, they can explore the &nbsp;entire history of the Thread with ease. This is all really similar to &nbsp;git commit speak, in which one only needs to know about a single commit &nbsp;to be able to trace back the entire history of a code project; it’s also &nbsp;essentially how blockchains work.</p> <h4>5. Content Addressing — store everything on IPFS and get ready to&nbsp;scale</h4> <p>As &nbsp;we alluded to earlier, each update to a Thread is backed by an IPFS CID &nbsp;hash (i.e., they are content addressable chunks of data on IPFS). This &nbsp;means <em>where</em> the data is stored &nbsp;is no longer relevant… IPFS will find it on the network via it’s hash. &nbsp;This helps us address our fifth requirement, that we have a <em>way to link updates via their content, rather than where they are stored</em>. We’ve covered this topic a lot <a href="https://medium.com/textileio">in the past</a>, but for the uninitiated, the next paragraph provides a summary of how content addressing on IPFS works (pulled from <a href="https://medium.com/textileio/enabling-the-distributed-web-abf7ab33b638">this previous article</a>).</p> <p>Rather than referencing a file or chunk of data by its location (think HTTP), we reference it via its <a href="https://en.wikipedia.org/wiki/Fingerprint_%28computing%29">fingerprint</a>. In IPFS and other such systems, this means identifying content by its cryptographic hash, or even better, a <a href="https://github.com/ipld/cid"><em>self-describing </em>content-addressed identifier</a> (<a href="https://github.com/multiformats/multihash">multihash</a>). A cryptographic hash is a (relatively) short alphanumeric string that’s calculated by running your content through a <a href="https://en.wikipedia.org/wiki/Cryptographic_hash_function">cryptographic hash function</a> (like <a href="https://en.wikipedia.org/wiki/SHA-3">SHA</a>). For example, when the (unencrypted) <a href="https://ipfs.io/ipfs/QmcNzGMQ1hUzSnqM2aH1Um8KWLRC4Rd9TyY4kFuYZXWXaF/Textile_Icon_500x500.png">Textile logo</a> is added to IPFS, its multihash ends up being <code>QmbgGgWW3vH7v9FDxVCzcouKGChqGEjtf6YLDUgSHnk5J2</code>. This ‘hash’ is actually the CID (Content IDentifier) for that file, computed from the <em>raw data </em>within that PNG. It is guaranteed to be cryptographically unique to the contents of <em>that </em>file, and that file only. If we change that file by even one bit, the hash will become something completely different.</p> <p>Now, &nbsp;when we want to access a file over IPFS (like the above logo), we can &nbsp;simply ask the IPFS network for the file with that exact CID, the &nbsp;network will find the peers that have the data (using a DHT), retrieve &nbsp;it, and verify (using the CID) that it’s the correct file. What this &nbsp;means is we can technically get the file from <em>multiple </em>places &nbsp;because as long as the file matches the hash, we know we’re getting the &nbsp;right data. Which brings us to the solution to our final requirement… &nbsp;use IPFS! For now, Textile is maintaining a network of large, &nbsp;homogeneous, volunteer nodes (we call them <a href="https://github.com/textileio/textile-go">Cafe</a>s) &nbsp;to ‘pin’ and store content on IPFS. It is important to note here that &nbsp;the other nodes doing the pinning are the same as the nodes on your &nbsp;phone — Textile Nodes that offer a pinning service to other peers. Soon, &nbsp;we’ll allow users to elect their own Cafe nodes, add even add &nbsp;additional nodes for redundancy. All this could eventually be driven by &nbsp;Filecoin for even greater scalablility and flexibility.</p> <h3>What’s Next?</h3> <p>So &nbsp;there you have it. Five solutions to five requirements for seamless, &nbsp;secure, decentralized photo sharing and backup. Easy 😉. And at a &nbsp;conceptual level, the Textile Thread protocol <em>is</em> &nbsp;relatively simple: blocks of operations chained together to produce a &nbsp;beautiful Thread of photos. But there’s a lot of complexity going on &nbsp;under-the-hood that has required a lot of experimentation, testing, and &nbsp;limit pushing, especially on mobile. And our journey isn’t over yet.</p> <p><a href="https://twitter.com/Textile01/lists/textile-team/members">The Textile team</a> &nbsp;is still hard at work iterating, updating, and improving upon what we &nbsp;already have working. For example, we’ll soon to moving to a new offline &nbsp;messaging system that allow us to drop the custom DHT fork, and move &nbsp;back to the public IPFS network. On top of this, our <a href="https://github.com/textileio/textile-go/projects/2">move to more powerful backup</a> &nbsp;and recovery capabilities has us taking new approaches to security, &nbsp;profile management, offline interactions, and much much more. On top of &nbsp;these changes, the team is actively working to modularize the Threads &nbsp;concept and code into its own stand-alone package, which should provide &nbsp;developers with something akin to a Realm and/or Firebase layer for &nbsp;decentralized mobile applications!</p> <p>If you are interested in learning more about this stuff, reach out over <a href="https://twitter.com/Textile01">Twitter</a> or <a href="https://slack.textile.io/">Slack</a>, &nbsp;or pull us aside the next time you see us at a conference or event. We’re happy to provide background, thoughts, and opinions on how we &nbsp;think the future of decentralized apps will play out. In the mean time, &nbsp;don’t forget to check out our <a href="https://github.com/textileio">GitHub repos</a> &nbsp;for code and PRs that showcase our current and old implementations. We &nbsp;try to make sure all our development happens out in the open, so you can &nbsp;see things as they develop. Additionally, if you haven’t already, <a href="https://www.textile.photos/#cta">don’t miss out on signing up for our waitlist</a>, where you can get early access to Textile Photos, the beautiful interface to Textile’s Threads.</p> </html>
titleTextile Threads whitepaper… just kidding… a deeper look at the tech behind Textile’s Threads protocol
authorsanderpick
permlinktextile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol
json metadata{"tags":["decentralization","p2p","crypto","blockchain","mobile"],"image":["https://cdn-images-1.medium.com/max/800/1*beyK889WqmNEatN55oBAnA.png","https://cdn-images-1.medium.com/max/800/1*xEE3Pp-LdU_r8qsPvMVS8w.gif","https://cdn-images-1.medium.com/max/800/1*0iCOZW6HrpdQ0izG5O9ciw.png","https://cdn-images-1.medium.com/max/800/1*JsXZAveyMui2izg_-FMh0g.png","https://cdn-images-1.medium.com/max/800/1*h9eNwOauuT7mMeS4o-yGbw.png","https://cdn-images-1.medium.com/max/800/1*3EuEtFHqUtALTczIeZ8_eg.png"],"links":["https://medium.com/@carsonfarmer","https://medium.com/@sanderpick","https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14","https://realm.io/","https://firebase.google.com/","https://textile.photos/","https://en.wikipedia.org/wiki/Bitcoin","https://bitcoin.org/en/developer-guide#initial-block-download","https://filecoin.io/","https://en.wikipedia.org/wiki/Peer-to-peer","https://en.wikipedia.org/wiki/Lists_of_network_protocols","https://libp2p.io/","https://ipfs.io/","https://github.com/libp2p/go-libp2p-secio","https://github.com/libp2p/go-stream-security","https://developers.google.com/protocol-buffers/","https://github.com/textileio/textile-go/blob/master/pb/protos/thread.proto","https://github.com/auditdrivencrypto/secure-channel/blob/master/prior-art.md#ipfss-secure-channel","https://github.com/textileio/textile-go/commit/05a269cd5dd72da224c4d2c472abad00a078d4ca","https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14#ea20","https://en.wikipedia.org/wiki/Kademlia","https://en.wikipedia.org/wiki/Distributed_hash_table","https://github.com/libp2p/go-libp2p-kad-dht","https://en.wikipedia.org/wiki/Hash_table","https://medium.com/textileio/swapping-bits-and-distributing-hashes-on-the-decentralized-web-5da98a3507","https://www.researchgate.net/figure/2-Overlay-and-underlay-view-of-Distributed-Hash-Tables-11_fig2_304495476","https://github.com/libp2p/notes/issues/2#issuecomment-433729343","https://github.com/textileio/textile-go/projects/2","https://github.com/ipfs/research-CRDT/","https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type","https://github.com/orbitdb/ipfs-log","https://www.atlassian.com/git/tutorials/using-branches/git-merge","https://blog.thoughtram.io/git/2014/11/18/the-anatomy-of-a-git-commit.html#meet-the-tree-object","https://twitter.com/Textile01/status/1004436869734543360","https://en.wikipedia.org/wiki/Blockchain","https://medium.com/textileio","https://medium.com/textileio/enabling-the-distributed-web-abf7ab33b638","https://en.wikipedia.org/wiki/Fingerprint_%28computing%29","https://github.com/ipld/cid","https://github.com/multiformats/multihash","https://en.wikipedia.org/wiki/Cryptographic_hash_function","https://en.wikipedia.org/wiki/SHA-3","https://ipfs.io/ipfs/QmcNzGMQ1hUzSnqM2aH1Um8KWLRC4Rd9TyY4kFuYZXWXaF/Textile_Icon_500x500.png","https://github.com/textileio/textile-go","https://twitter.com/Textile01/lists/textile-team/members","https://twitter.com/Textile01","https://slack.textile.io/","https://github.com/textileio","https://www.textile.photos/#cta"],"app":"steemit/0.1","format":"html"}
parent author
parent permlinkmobile
Transaction InfoBlock #27323361/Trx cc5a1c09f224dae0c0de896393c778b894bd8fca
View Raw JSON Data
{
  "op": [
    "comment",
    {
      "body": "<html>\n<p><em>Written by </em><a href=\"https://medium.com/@carsonfarmer\"><em>Carson Farmer</em></a><em> &amp; </em><a href=\"https://medium.com/@sanderpick\"><em>Sander Pick</em></a></p>\n<p><center><img src=\"https://cdn-images-1.medium.com/max/800/1*beyK889WqmNEatN55oBAnA.png\" width=\"800\" height=\"565\"/><em>Download the app, take a picture, share!</em></center></p>\n<p>Recently, &nbsp;we’ve started writing more about the technologies underlying Textile &nbsp;Photos that help keep your photos (and likes and comments, etc) safe and &nbsp;secure on the decentralized web. In our previous post, we talked about <a href=\"https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14\">the encryption process Textile Photos</a>, &nbsp;with a focus how Textile delivers end-to-end encrypted photo sharing. &nbsp;Today’s post is a follow-up (though it should also be sufficiently &nbsp;detailed to stand on its own), this time highlighting how Textile &nbsp;coordinates private photo sharing among groups of users, a feature we &nbsp;call <em>Threads</em>.</p>\n<p><strong>Why we built it</strong><br>\nWe &nbsp;designed Threads to allow groups of users to share photos securely and &nbsp;privately, without any centralized, authoritative database. We also made &nbsp;sure it all works well offline, that its possible to recover lost data, &nbsp;and that its easy to add new members.</p>\n<p><strong>What makes Threads different</strong><br>\nThreads &nbsp;allow private groups to post photos and interact over a decentralized &nbsp;network, maintaining complete control over their own content. Textile &nbsp;operates in a completely zero-knowledge framework. Private by design.</p>\n<p><strong>Why Threads are exciting</strong><br>\nBecause &nbsp;photos are just the first step. Today, Threads allow users to share a &nbsp;photo with other Thread in a secure, decentralized way. Threads can &nbsp;facilitate secure sharing, coordination, and storage of <em>many</em> types of data over a decentralized network. Upgradable by design.</p>\n<p>On &nbsp;the surface, you can think of each Thread like a decentralized &nbsp;database, shared between specific participants. We built Threads into &nbsp;the fabric of Textile (see what we did there 😉) because group members &nbsp;need a record of who shared what photo, and when. But, once we created &nbsp;Threads, we realized just how powerful a concept this was — for those &nbsp;familiar with mobile app development, think <a href=\"https://realm.io/\">Realm</a> or <a href=\"https://firebase.google.com/\">Firebase</a> but <em>without</em> the centralized server.</p>\n<p>To &nbsp;really understand what Threads brings to the table, you really need to &nbsp;understand Threads themselves. So let’s dig a bit deeper into how &nbsp;Textile conceptualizes and implements Threads, and how that helps keep &nbsp;your photos (and likes and comments, etc) safe and secure on the &nbsp;decentralized web. We’ll start by highlighting the specific requirements &nbsp;we had when developing Threads, and then break down each of these &nbsp;requirements into the specific solutions that we came up with. Along the &nbsp;way, our CTO Sander Pick will highlight how those various solutions &nbsp;came about, and why we think our approach is in the best interest of our &nbsp;users.</p>\n<h3>The experience</h3>\n<p><a href=\"https://textile.photos/\">Textile Photos</a> &nbsp;allows small, decentralized, private groups to share photos, send &nbsp;messages, and engage with each other. That’s the experience, so it has &nbsp;to ‘just work’.</p>\n<p><center><img src=\"https://cdn-images-1.medium.com/max/800/1*xEE3Pp-LdU_r8qsPvMVS8w.gif\" width=\"400\" height=\"600\"/></center></p>\n<h3>Requirements</h3>\n<p>To drive the Textile user experience, we identified five key features needed in the sharing protocols:</p>\n<ol>\n  <li><strong>A mechanism to share and receive state updates within a group of n users</strong><br>\nTo &nbsp;enable photo sharing (and other common interactions such as likes and &nbsp;comments) among a group of friends and/or colleagues, some concept of a <em>shared state</em> is required.</li>\n  <li><strong>A way to ensure the shared state stays resilient to peers dropping out or latency issues</strong><br>\nSince &nbsp;we’re operating in a mobile environment, we have to expect peers to &nbsp;continually drop ‘offline’ due to coverage issues, app back-grounding, &nbsp;battery optimizations, and a whole slew of other reasons for a mobile &nbsp;device to be cut off from a network.</li>\n  <li><strong>A way to avoid state conflicts with other members of the group</strong><br>\nOn &nbsp;top of the requirements above, when peers do come back online, we don’t &nbsp;want any state changes that were made by other members of the group &nbsp;while they were disconnected from the network to conflict with their own &nbsp;local changes.</li>\n  <li><strong>A mechanism to recover the full state from the network as a whole</strong><br>\nAnother &nbsp;important consideration in the mobile world is that the number of users &nbsp;(out of n) that are online at any given time is generally unknown, and &nbsp;quite possibly zero. To reiterate, we want a decentralized shared state, &nbsp;but it has to work <em>even when you are the only member online</em>. &nbsp;This means we have to assume the full group state may not ever be &nbsp;directly accessible (i.e., downloadable) from a single group member. &nbsp;This is in contrast to something like <a href=\"https://en.wikipedia.org/wiki/Bitcoin\">Bitcoin</a>, where new nodes are able to <a href=\"https://bitcoin.org/en/developer-guide#initial-block-download\">download the full blockchain</a> from any connected peer.</li>\n  <li><strong>Way to link updates via their content, rather than where they are stored</strong><br>\nSince we are building on top of the IPFS network, and would like to eventually support a <a href=\"https://filecoin.io/\">Filecoin</a>-based &nbsp;future in which users can select from a multitude of decentralized &nbsp;storage providers, Threads need to embrace content addressing, rather &nbsp;than location addressing. This makes it easy to grow and change the &nbsp;underlying network, without affecting data access and sharing.</li>\n</ol>\n<p>With these requirements in mind, let’s break down our solutions into their individual components…</p>\n<h3>Solutions</h3>\n<h4>1. Handling Updates — use a peer-to-peer network with structured updates</h4>\n<p>First things first: <em>how do we handle state updates between a set of distributed peers?</em> This is mostly about <a href=\"https://en.wikipedia.org/wiki/Peer-to-peer\">peer-to-peer (p2p) networking</a>. &nbsp;And when it comes to communicating between heterogeneous network &nbsp;devices (computers, phones, IoT devices, etc), we actually need many <a href=\"https://en.wikipedia.org/wiki/Lists_of_network_protocols\">different types of network protocols</a>. &nbsp;That way, no matter what type of device we are talking about — be it a &nbsp;phone, desktop computer, browser, or Internet-enabled fridge — it is &nbsp;able to communicate with other devices located in the same room, or on &nbsp;the other side of the planet.</p>\n<p>At Textile, we use the super amazing <a href=\"https://libp2p.io/\">libp2p</a> &nbsp;library for our networking needs. Libp2p is a networking stack and &nbsp;library (you might have heard it called a protocol suite) modularized &nbsp;out of the <a href=\"https://ipfs.io/\">IPFS project</a>, &nbsp;and bundled separately for other tools to use. Essentially, libp2p does &nbsp;all the heavy network lifting so that we can focus on our core task: &nbsp;exchanging updates between communicating peers.</p>\n<p><center><img src=\"https://cdn-images-1.medium.com/max/800/1*0iCOZW6HrpdQ0izG5O9ciw.png\" width=\"800\" height=\"316\"/><em>Decentralized, peer-to-peer networks are radically different types of communication networks.</em></center></p>\n<blockquote>Libp2p &nbsp;was a pretty natural choice for us. The stack includes all the crypto &nbsp;and networking protocols we need to deliver messages to group members, &nbsp;and the libp2p developer community is super responsive and excited about &nbsp;the power of p2p interactions. Easy choice.</blockquote>\n<p>The &nbsp;other really nice thing about using the libp2p library is it comes &nbsp;packed with many useful cryptography tools and functions, keeping &nbsp;communications secure. For instance, all p2p communications over the &nbsp;Textile network use the <a href=\"https://github.com/libp2p/go-libp2p-secio\">secio</a> <a href=\"https://github.com/libp2p/go-stream-security\">stream security transport</a>. &nbsp;This way, all connections use secure sessions provided by libp2p/secio &nbsp;to encrypt all traffic, whereby a TLS-like handshake is used to setup &nbsp;the initial communication channel.</p>\n<p>Like many IPFS-based projects, Textile uses <a href=\"https://developers.google.com/protocol-buffers/\">Protocol Buffers</a> &nbsp;for over-the-wire communication, and advanced cryptographic algorithms &nbsp;to secure those messages. Essentially, each update to the shared group &nbsp;state is just an encrypted Profobuf message with two parts: a header &nbsp;with author and date info, and a body with the type-specific data. These &nbsp;pieces are sent in their own inner-’envelope’ which contains a link to &nbsp;the encrypted message and the Thread ID. This inner-envelope is then &nbsp;signed by the sender and placed into the wire ‘envelope’ along with it’s &nbsp;signature. You can read more about some of the cryptographic tools &nbsp;Textile uses in <a href=\"https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14\">this previous article</a>. You can also check out how we structure our <a href=\"https://github.com/textileio/textile-go/blob/master/pb/protos/thread.proto\">Protobuf messages</a>, learn a bit more about <a href=\"https://github.com/auditdrivencrypto/secure-channel/blob/master/prior-art.md#ipfss-secure-channel\">how secio works</a>, plus check out some <a href=\"https://github.com/textileio/textile-go/commit/05a269cd5dd72da224c4d2c472abad00a078d4ca\">recent updates</a> to message encryption while you’re at it.</p>\n<h4>2. Network Resilience — support offline messaging so peers can come and&nbsp;go</h4>\n<p>If you are at all familiar with libp2p, then you might be thinking <em>“ah libp2p has a pubsub layer that would be perfect for exchanging updates to a group of connecting peers”</em>. &nbsp;And while you’d certainly be right, there are a few key limitations &nbsp;that makes using pubsub for something like Textile Photos pretty &nbsp;cumbersome. On top of this, while pubsub is super nice for things like &nbsp;chat rooms or distributed services, it is a ‘fire-and-forget’ messaging &nbsp;protocol, meaning that once a peer publishes a message, it is up to its &nbsp;peers to ensure they are listening for the right message at the right &nbsp;time. To circumvent this, some pubsub systems introduce message echoing, &nbsp;to ensure a message stays in the system long enough to be picked up by &nbsp;the peers who might need it. However, this can lead to really noisy &nbsp;network traffic, and is really just a band-aid over a larger issue.</p>\n<blockquote>Our &nbsp;initial POC involved pubsub and always-online room echoers… not &nbsp;scalable or particularly decentralized. A real solution to distributing &nbsp;state has to involve direct messaging with an offline mechanism.</blockquote>\n<p>So this starts to get at our second requirement, that <em>the shared state stays resilient to peers dropping out</em>. &nbsp;We need to assume peers might not be around to receive important &nbsp;messages in ‘real-time’, which is a common problem with p2p systems. &nbsp;Right now, Textile addresses this problem by enabling what you might &nbsp;call <em>offline messaging</em>. Since we’re <a href=\"https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14#ea20\">already using IPFS for data storage and communication</a>, &nbsp;we wanted to take advantage of some of the core technologies driving &nbsp;IPFS. In particular, we (currently) use a special fork of the <a href=\"https://en.wikipedia.org/wiki/Kademlia\">Kademlia</a>-based <a href=\"https://en.wikipedia.org/wiki/Distributed_hash_table\">distributed hash table</a> (DHT) <a href=\"https://github.com/libp2p/go-libp2p-kad-dht\">used by IPFS, </a>that allows us to post messages for a peer directly in the DHT. For those unfamiliar with DHTs, they are a <a href=\"https://en.wikipedia.org/wiki/Hash_table\">hash table</a> &nbsp;where the data is spread across a network of nodes or peers. And these &nbsp;peers are all coordinated to enable efficient access and lookup between &nbsp;nodes in a decentralized way. You can read more about this kind of stuff &nbsp;in our previous article about <a href=\"https://medium.com/textileio/swapping-bits-and-distributing-hashes-on-the-decentralized-web-5da98a3507\">how IPFS peers find, request, and retrieve content (and each other) on the decentralized web</a>. &nbsp;So, when a peer we want to communicate with is offline, rather than &nbsp;blindly sending them a message that will never be received, we post a &nbsp;message to Textile’s DHT, and they can then retrieve that message the &nbsp;next time they come online again. Conceptually simple, and works pretty &nbsp;well in practice.</p>\n<p><center><img src=\"https://cdn-images-1.medium.com/max/800/1*JsXZAveyMui2izg_-FMh0g.png\" width=\"800\" height=\"485\"/><em>p2p &nbsp;network with custom Textile DHT overlay. Peers post (key, value) &nbsp;messages (value) with a key specific to their intended recipient, and &nbsp;this key is broadcast and available to entire network; though only the &nbsp;intended recipient is able to decrypt the actual message content. Based &nbsp;on Figure 1–2 from </em><a href=\"https://www.researchgate.net/figure/2-Overlay-and-underlay-view-of-Distributed-Hash-Tables-11_fig2_304495476\"><em>this&nbsp;thesis</em></a><em>.</em></center></p>\n<p>There &nbsp;are still some issues with our current approach, including that it is &nbsp;difficult/impossible to remove messages from the DHT manually. Indeed, &nbsp;it can start to get a bit messy when left-over offline messages have to &nbsp;be retrieved each time a peer comes back online… imagine a peer that &nbsp;goes in and out of service frequently, this could lead to a lot of &nbsp;network traffic and wasted CPU cycles. So, we’ve <a href=\"https://github.com/libp2p/notes/issues/2#issuecomment-433729343\">implemented an alternative</a> &nbsp;to this DHT-based offline messaging system that does not suffer from &nbsp;these limitations (and also allows us to participate in the public IPFS &nbsp;network), while still remaining decentralized and scalable in the &nbsp;long-term. This new approach should be released soon, after more testing &nbsp;and evaluation. You can follow along with this progress as part of the <a href=\"https://github.com/textileio/textile-go/projects/2\">move towards a Cafe-based setup</a> (see also <strong>What’s Next</strong>).</p>\n<h4>3 &amp; 4. Avoiding Conflicts &amp; State Recovery —use a CRDT to keep an immutable history across&nbsp;peers</h4>\n<p>Ok, so our next requirement and its associated solution have received a <a href=\"https://github.com/ipfs/research-CRDT/\">great deal of research and development attention over the years</a>. The question of “<em>how to avoid state conflicts with other members of a group?”</em> &nbsp;comes up when working collaboratively on documents, updating shared &nbsp;databases, etc. For the purposes of updating a shared Thread of photos, &nbsp;it turns out that an <a href=\"https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type\">operation-based CRDT</a> &nbsp;that supports append-only operations is pretty much all you need to get &nbsp;going. You can think of Textile’s CRDT (which shares some ideas with <a href=\"https://github.com/orbitdb/ipfs-log\">ipfs-log</a>) &nbsp;setup as an immutable, append-only tree that can be used to model a &nbsp;mutable, shared state between peers. Every entry in the tree is saved on &nbsp;IPFS, and each points to a hash of previous entry(ies) forming a graph. &nbsp;These trees can be <a href=\"https://www.atlassian.com/git/tutorials/using-branches/git-merge\">3-way and fast-forward merged</a>.</p>\n<p>Speaking &nbsp;of forks and joins, for those familiar with git and other similar &nbsp;system, you might be thinking this sounds a lot like a <a href=\"https://blog.thoughtram.io/git/2014/11/18/the-anatomy-of-a-git-commit.html#meet-the-tree-object\">git hash tree</a>, <a href=\"https://twitter.com/Textile01/status/1004436869734543360\">Merkle DAG</a>, or even a <a href=\"https://en.wikipedia.org/wiki/Blockchain\">blockchain</a>. &nbsp;And you’d be right! The concepts are very similar, and this buys us &nbsp;some really nice properties for building and maintaining a shared state. &nbsp;By modeling our shared Thread state in this way, we benefit from tried &nbsp;and tested methods for allowing a peer to incorporate other peers’ &nbsp;updates into their state while maintaining history (via fast-forwards &nbsp;and three-way merging for example).</p>\n<blockquote>At the end of the day, a Thread is just a git-like hash tree of updates with a deterministic merge policy. Simple.</blockquote>\n<p>So &nbsp;what does this look like in practice? Currently — because things might &nbsp;change as we make improvements to the underlying implementation — each &nbsp;Thread in Textile Photos is essentially a chain of updates, where each &nbsp;update represents some specific action or event. For instance, when you &nbsp;create a new Thread, under-the-hood you are actually creating a <code>JOIN</code> update on a new Thread chain. Similarly, when you update the Thread via a new photo (<code>DATA</code> update), comment, or like (<code>ANNOTATION</code> update), you’re actually updating that Thread chain. After each modification, the <code>HEAD</code> of the Thread will point to the latest update.</p>\n<p>Building on top of these ideas, we also have concepts such as an <code>INVITE</code>, which points a new peer to a given point on the Thread chain, or a <code>MERGE</code>, which happens when the current <code>HEAD</code> &nbsp;is not contained in an incoming update’s parent list for some reason &nbsp;(maybe the peer doesn’t know about it because they were offline). If two &nbsp;peers are merging the <em>same sub trees</em>, &nbsp;all they need to do to ensure the update resolves to the same hash is &nbsp;a) include the same date b) exclude author info. To get the same date, &nbsp;they both follow a rule: choose the latest of the parents for the date &nbsp;(in practice they add a little bit extra on to keep it ahead of both &nbsp;parents).</p>\n<p>To give you a better idea of what exactly we’re talking about, consider the following set of operations: <code>User A</code> creates a new Thread, and adds a Photo. They then externally invite <code>User B</code> (sent via some other <em>secure</em> communication channel), who eventually joins the Thread. But before <code>User B</code> is able to join the Thread, <code>User A</code> adds another Photo, moving the Thread’s <code>HEAD</code> forward. By the time <code>User B</code> joins the Thread, they’d end up with a Thread sequence that looks something like this:</p>\n<p><center><img src=\"https://cdn-images-1.medium.com/max/800/1*h9eNwOauuT7mMeS4o-yGbw.png\" width=\"800\" height=\"478\"/><em>Thread &nbsp;join example. Solid arrows point towards the ‘parent’ of a given &nbsp;update, over-the-wire communications are indicated with a 📶-style &nbsp;arrow, and messages that are rebroadcast (e.g., via the welcome message) &nbsp;are indicated with a dashed arrow. Similarly, merges point to both &nbsp;their parent&nbsp;updates.</em></center></p>\n<p>Here, &nbsp;we see the merge happening at the end of the sequence because the &nbsp;bottom peer is joining via an external invite that is no longer <code>HEAD</code>&nbsp;, forcing them to merge the most recent <code>DATA</code> update with their own <code>JOIN</code> update. But since merge results are deterministic (given the same parents), both peers create the <code>MERGE</code> update locally, and do not broadcast them to avoid trading merges back and forth.</p>\n<p>A more complete sequence is given in the following figure. Suppose <code>User A</code> &nbsp;goes ‘offline’ (e.g., their phone goes to sleep, they shut down the &nbsp;app, they lose their data connection, etc), and in the mean time, both<code> Users A</code> and <code>B</code> update the Thread, with <code>User A</code> adding an <code>ANNOTATION</code> update, and <code>User B</code> adding a new Photo (<code>DATA</code> update). Now, when <code>User A</code> comes back online, there is a conflict, and both Users create a <code>MERGE</code> update to remedy this. A <code>MERGE</code> update has two parents, in this case, the <code>DATA</code> and <code>ANNOTATION</code> update from the different users. As always, the <code>HEAD</code> continues to point to the latest update (which in the example below eventually becomes an <code>ANNOTATION</code> from <code>User B</code>). Once both peers are online again, the more straightforward update and transmit mode of operation can continue.</p>\n<p><center><img src=\"https://cdn-images-1.medium.com/max/800/1*3EuEtFHqUtALTczIeZ8_eg.png\" width=\"800\" height=\"473\"/><em>More &nbsp;complex Thread interaction where one or more peers are temporarily &nbsp;offline. Note that an external invite is the same as a normal invite, &nbsp;but the invite details are encrypted with a single use key, which is &nbsp;sharable with the invite update location.</em></center></p>\n<p>The &nbsp;same properties that make hash trees or blockchains useful for &nbsp;developing a shared, consistent (consensus-driven) state, also makes it &nbsp;possible to address our fourth requirement: <em>the ability to recover the full state from the network as a whole</em>. &nbsp;Because each Thread update references its parent(s), given a single &nbsp;point on the Thread chain, we can trace back all the way to the &nbsp;beginning of the Thread. For example, at any point along the sequence in &nbsp;the above figures, a peer can trace back the history of the Thread, as &nbsp;indicated by the solid arrows. This works particularly nicely when a &nbsp;peer <code>JOIN</code>s a thread, even at a point prior to the current <code>HEAD</code>. They can simply <code>JOIN</code>, and any existing Thread member can send them the latest <code>HEAD</code> &nbsp;(even via offline messages if needed). From here, they can explore the &nbsp;entire history of the Thread with ease. This is all really similar to &nbsp;git commit speak, in which one only needs to know about a single commit &nbsp;to be able to trace back the entire history of a code project; it’s also &nbsp;essentially how blockchains work.</p>\n<h4>5. Content Addressing — store everything on IPFS and get ready to&nbsp;scale</h4>\n<p>As &nbsp;we alluded to earlier, each update to a Thread is backed by an IPFS CID &nbsp;hash (i.e., they are content addressable chunks of data on IPFS). This &nbsp;means <em>where</em> the data is stored &nbsp;is no longer relevant… IPFS will find it on the network via it’s hash. &nbsp;This helps us address our fifth requirement, that we have a <em>way to link updates via their content, rather than where they are stored</em>. We’ve covered this topic a lot <a href=\"https://medium.com/textileio\">in the past</a>, but for the uninitiated, the next paragraph provides a summary of how content addressing on IPFS works (pulled from <a href=\"https://medium.com/textileio/enabling-the-distributed-web-abf7ab33b638\">this previous article</a>).</p>\n<p>Rather than referencing a file or chunk of data by its location (think HTTP), we reference it via its <a href=\"https://en.wikipedia.org/wiki/Fingerprint_%28computing%29\">fingerprint</a>. In IPFS and other such systems, this means identifying content by its cryptographic hash, or even better, a <a href=\"https://github.com/ipld/cid\"><em>self-describing </em>content-addressed identifier</a> (<a href=\"https://github.com/multiformats/multihash\">multihash</a>). A cryptographic hash is a (relatively) short alphanumeric string that’s calculated by running your content through a <a href=\"https://en.wikipedia.org/wiki/Cryptographic_hash_function\">cryptographic hash function</a> (like <a href=\"https://en.wikipedia.org/wiki/SHA-3\">SHA</a>). For example, when the (unencrypted) <a href=\"https://ipfs.io/ipfs/QmcNzGMQ1hUzSnqM2aH1Um8KWLRC4Rd9TyY4kFuYZXWXaF/Textile_Icon_500x500.png\">Textile logo</a> is added to IPFS, its multihash ends up being <code>QmbgGgWW3vH7v9FDxVCzcouKGChqGEjtf6YLDUgSHnk5J2</code>. This ‘hash’ is actually the CID (Content IDentifier) for that file, computed from the <em>raw data </em>within that PNG. It is guaranteed to be cryptographically unique to the contents of <em>that </em>file, and that file only. If we change that file by even one bit, the hash will become something completely different.</p>\n<p>Now, &nbsp;when we want to access a file over IPFS (like the above logo), we can &nbsp;simply ask the IPFS network for the file with that exact CID, the &nbsp;network will find the peers that have the data (using a DHT), retrieve &nbsp;it, and verify (using the CID) that it’s the correct file. What this &nbsp;means is we can technically get the file from <em>multiple </em>places &nbsp;because as long as the file matches the hash, we know we’re getting the &nbsp;right data. Which brings us to the solution to our final requirement… &nbsp;use IPFS! For now, Textile is maintaining a network of large, &nbsp;homogeneous, volunteer nodes (we call them <a href=\"https://github.com/textileio/textile-go\">Cafe</a>s) &nbsp;to ‘pin’ and store content on IPFS. It is important to note here that &nbsp;the other nodes doing the pinning are the same as the nodes on your &nbsp;phone — Textile Nodes that offer a pinning service to other peers. Soon, &nbsp;we’ll allow users to elect their own Cafe nodes, add even add &nbsp;additional nodes for redundancy. All this could eventually be driven by &nbsp;Filecoin for even greater scalablility and flexibility.</p>\n<h3>What’s Next?</h3>\n<p>So &nbsp;there you have it. Five solutions to five requirements for seamless, &nbsp;secure, decentralized photo sharing and backup. Easy 😉. And at a &nbsp;conceptual level, the Textile Thread protocol <em>is</em> &nbsp;relatively simple: blocks of operations chained together to produce a &nbsp;beautiful Thread of photos. But there’s a lot of complexity going on &nbsp;under-the-hood that has required a lot of experimentation, testing, and &nbsp;limit pushing, especially on mobile. And our journey isn’t over yet.</p>\n<p><a href=\"https://twitter.com/Textile01/lists/textile-team/members\">The Textile team</a> &nbsp;is still hard at work iterating, updating, and improving upon what we &nbsp;already have working. For example, we’ll soon to moving to a new offline &nbsp;messaging system that allow us to drop the custom DHT fork, and move &nbsp;back to the public IPFS network. On top of this, our <a href=\"https://github.com/textileio/textile-go/projects/2\">move to more powerful backup</a> &nbsp;and recovery capabilities has us taking new approaches to security, &nbsp;profile management, offline interactions, and much much more. On top of &nbsp;these changes, the team is actively working to modularize the Threads &nbsp;concept and code into its own stand-alone package, which should provide &nbsp;developers with something akin to a Realm and/or Firebase layer for &nbsp;decentralized mobile applications!</p>\n<p>If you are interested in learning more about this stuff, reach out over <a href=\"https://twitter.com/Textile01\">Twitter</a> or <a href=\"https://slack.textile.io/\">Slack</a>, &nbsp;or pull us aside the next time you see us at a conference or event. We’re happy to provide background, thoughts, and opinions on how we &nbsp;think the future of decentralized apps will play out. In the mean time, &nbsp;don’t forget to check out our <a href=\"https://github.com/textileio\">GitHub repos</a> &nbsp;for code and PRs that showcase our current and old implementations. We &nbsp;try to make sure all our development happens out in the open, so you can &nbsp;see things as they develop. Additionally, if you haven’t already, <a href=\"https://www.textile.photos/#cta\">don’t miss out on signing up for our waitlist</a>, where you can get early access to Textile Photos, the beautiful interface to Textile’s Threads.</p>\n</html>",
      "title": "Textile Threads whitepaper… just kidding… a deeper look at the tech behind Textile’s Threads protocol",
      "author": "sanderpick",
      "permlink": "textile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol",
      "json_metadata": "{\"tags\":[\"decentralization\",\"p2p\",\"crypto\",\"blockchain\",\"mobile\"],\"image\":[\"https://cdn-images-1.medium.com/max/800/1*beyK889WqmNEatN55oBAnA.png\",\"https://cdn-images-1.medium.com/max/800/1*xEE3Pp-LdU_r8qsPvMVS8w.gif\",\"https://cdn-images-1.medium.com/max/800/1*0iCOZW6HrpdQ0izG5O9ciw.png\",\"https://cdn-images-1.medium.com/max/800/1*JsXZAveyMui2izg_-FMh0g.png\",\"https://cdn-images-1.medium.com/max/800/1*h9eNwOauuT7mMeS4o-yGbw.png\",\"https://cdn-images-1.medium.com/max/800/1*3EuEtFHqUtALTczIeZ8_eg.png\"],\"links\":[\"https://medium.com/@carsonfarmer\",\"https://medium.com/@sanderpick\",\"https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14\",\"https://realm.io/\",\"https://firebase.google.com/\",\"https://textile.photos/\",\"https://en.wikipedia.org/wiki/Bitcoin\",\"https://bitcoin.org/en/developer-guide#initial-block-download\",\"https://filecoin.io/\",\"https://en.wikipedia.org/wiki/Peer-to-peer\",\"https://en.wikipedia.org/wiki/Lists_of_network_protocols\",\"https://libp2p.io/\",\"https://ipfs.io/\",\"https://github.com/libp2p/go-libp2p-secio\",\"https://github.com/libp2p/go-stream-security\",\"https://developers.google.com/protocol-buffers/\",\"https://github.com/textileio/textile-go/blob/master/pb/protos/thread.proto\",\"https://github.com/auditdrivencrypto/secure-channel/blob/master/prior-art.md#ipfss-secure-channel\",\"https://github.com/textileio/textile-go/commit/05a269cd5dd72da224c4d2c472abad00a078d4ca\",\"https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14#ea20\",\"https://en.wikipedia.org/wiki/Kademlia\",\"https://en.wikipedia.org/wiki/Distributed_hash_table\",\"https://github.com/libp2p/go-libp2p-kad-dht\",\"https://en.wikipedia.org/wiki/Hash_table\",\"https://medium.com/textileio/swapping-bits-and-distributing-hashes-on-the-decentralized-web-5da98a3507\",\"https://www.researchgate.net/figure/2-Overlay-and-underlay-view-of-Distributed-Hash-Tables-11_fig2_304495476\",\"https://github.com/libp2p/notes/issues/2#issuecomment-433729343\",\"https://github.com/textileio/textile-go/projects/2\",\"https://github.com/ipfs/research-CRDT/\",\"https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type\",\"https://github.com/orbitdb/ipfs-log\",\"https://www.atlassian.com/git/tutorials/using-branches/git-merge\",\"https://blog.thoughtram.io/git/2014/11/18/the-anatomy-of-a-git-commit.html#meet-the-tree-object\",\"https://twitter.com/Textile01/status/1004436869734543360\",\"https://en.wikipedia.org/wiki/Blockchain\",\"https://medium.com/textileio\",\"https://medium.com/textileio/enabling-the-distributed-web-abf7ab33b638\",\"https://en.wikipedia.org/wiki/Fingerprint_%28computing%29\",\"https://github.com/ipld/cid\",\"https://github.com/multiformats/multihash\",\"https://en.wikipedia.org/wiki/Cryptographic_hash_function\",\"https://en.wikipedia.org/wiki/SHA-3\",\"https://ipfs.io/ipfs/QmcNzGMQ1hUzSnqM2aH1Um8KWLRC4Rd9TyY4kFuYZXWXaF/Textile_Icon_500x500.png\",\"https://github.com/textileio/textile-go\",\"https://twitter.com/Textile01/lists/textile-team/members\",\"https://twitter.com/Textile01\",\"https://slack.textile.io/\",\"https://github.com/textileio\",\"https://www.textile.photos/#cta\"],\"app\":\"steemit/0.1\",\"format\":\"html\"}",
      "parent_author": "",
      "parent_permlink": "mobile"
    }
  ],
  "block": 27323361,
  "trx_id": "cc5a1c09f224dae0c0de896393c778b894bd8fca",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T16:37:36",
  "virtual_op": false,
  "trx_in_block": 9
}
2018/11/01 16:35:45
voterandrewxhill
authorsanderpick
weight1631 (16.31%)
rshares152762950
permlinktextile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol
pending payout0.000 HBD
total vote weight21389
Transaction InfoBlock #27323324/Trx 010d81815711283224501047e25064466a676724
View Raw JSON Data
{
  "op": [
    "effective_comment_vote",
    {
      "voter": "andrewxhill",
      "author": "sanderpick",
      "weight": 1631,
      "rshares": 152762950,
      "permlink": "textile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol",
      "pending_payout": "0.000 HBD",
      "total_vote_weight": 21389
    }
  ],
  "block": 27323324,
  "trx_id": "010d81815711283224501047e25064466a676724",
  "op_in_trx": 1,
  "timestamp": "2018-11-01T16:35:45",
  "virtual_op": true,
  "trx_in_block": 29
}
2018/11/01 16:35:45
voterandrewxhill
authorsanderpick
weight10000 (100.00%)
permlinktextile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol
Transaction InfoBlock #27323324/Trx 010d81815711283224501047e25064466a676724
View Raw JSON Data
{
  "op": [
    "vote",
    {
      "voter": "andrewxhill",
      "author": "sanderpick",
      "weight": 10000,
      "permlink": "textile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol"
    }
  ],
  "block": 27323324,
  "trx_id": "010d81815711283224501047e25064466a676724",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T16:35:45",
  "virtual_op": false,
  "trx_in_block": 29
}
2018/11/01 16:30:45
bodyHi! I am a robot. I just upvoted you! I found similar content that readers might be interested in: https://medium.com/textileio/wip-textile-threads-whitepaper-just-kidding-6ce3a6624338
title
authorcheetah
permlinkcheetah-re-sanderpicktextile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol
json metadata
parent authorsanderpick
parent permlinktextile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol
Transaction InfoBlock #27323224/Trx c0e5a992846d1346ed92138de94bb266e51bd1d5
View Raw JSON Data
{
  "op": [
    "comment",
    {
      "body": "Hi! I am a robot. I just upvoted you! I found similar content that readers might be interested in:\nhttps://medium.com/textileio/wip-textile-threads-whitepaper-just-kidding-6ce3a6624338",
      "title": "",
      "author": "cheetah",
      "permlink": "cheetah-re-sanderpicktextile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol",
      "json_metadata": "",
      "parent_author": "sanderpick",
      "parent_permlink": "textile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol"
    }
  ],
  "block": 27323224,
  "trx_id": "c0e5a992846d1346ed92138de94bb266e51bd1d5",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T16:30:45",
  "virtual_op": false,
  "trx_in_block": 2
}
2018/11/01 16:30:39
votercheetah
authorsanderpick
weight167 (1.67%)
rshares279678612
permlinktextile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol
pending payout0.000 HBD
total vote weight16727
Transaction InfoBlock #27323222/Trx 73b04dde954bafb42766967e8db1bb4e4a50833f
View Raw JSON Data
{
  "op": [
    "effective_comment_vote",
    {
      "voter": "cheetah",
      "author": "sanderpick",
      "weight": 167,
      "rshares": 279678612,
      "permlink": "textile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol",
      "pending_payout": "0.000 HBD",
      "total_vote_weight": 16727
    }
  ],
  "block": 27323222,
  "trx_id": "73b04dde954bafb42766967e8db1bb4e4a50833f",
  "op_in_trx": 1,
  "timestamp": "2018-11-01T16:30:39",
  "virtual_op": true,
  "trx_in_block": 22
}
2018/11/01 16:30:39
votercheetah
authorsanderpick
weight8 (0.08%)
permlinktextile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol
Transaction InfoBlock #27323222/Trx 73b04dde954bafb42766967e8db1bb4e4a50833f
View Raw JSON Data
{
  "op": [
    "vote",
    {
      "voter": "cheetah",
      "author": "sanderpick",
      "weight": 8,
      "permlink": "textile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol"
    }
  ],
  "block": 27323222,
  "trx_id": "73b04dde954bafb42766967e8db1bb4e4a50833f",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T16:30:39",
  "virtual_op": false,
  "trx_in_block": 22
}
2018/11/01 16:30:30
body<html> <p><em>Written by </em><a href="https://medium.com/@carsonfarmer"><em>Carson Farmer</em></a><em> &amp; </em><a href="https://medium.com/@sanderpick"><em>Sander Pick</em></a></p> <p><center><img src="https://cdn-images-1.medium.com/max/800/1*beyK889WqmNEatN55oBAnA.png" width="800" height="565"/><em>Download the app, take a picture, share!</em></center></p> <p>Recently, &nbsp;we’ve started writing more about the technologies underlying Textile &nbsp;Photos that help keep your photos (and likes and comments, etc) safe and &nbsp;secure on the decentralized web. In our previous post, we talked about <a href="https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14">the encryption process Textile Photos</a>, &nbsp;with a focus how Textile delivers end-to-end encrypted photo sharing. &nbsp;Today’s post is a follow-up (though it should also be sufficiently &nbsp;detailed to stand on its own), this time highlighting how Textile &nbsp;coordinates private photo sharing among groups of users, a feature we &nbsp;call <em>Threads</em>.</p> <p><strong>Why we built it</strong><br> We &nbsp;designed Threads to allow groups of users to share photos securely and &nbsp;privately, without any centralized, authoritative database. We also made &nbsp;sure it all works well offline, that its possible to recover lost data, &nbsp;and that its easy to add new members.</p> <p><strong>What makes Threads different</strong><br> Threads &nbsp;allow private groups to post photos and interact over a decentralized &nbsp;network, maintaining complete control over their own content. Textile &nbsp;operates in a completely zero-knowledge framework. Private by design.</p> <p><strong>Why Threads are exciting</strong><br> Because &nbsp;photos are just the first step. Today, Threads allow users to share a &nbsp;photo with other Thread in a secure, decentralized way. Threads can &nbsp;facilitate secure sharing, coordination, and storage of <em>many</em> types of data over a decentralized network. Upgradable by design.</p> <p>On &nbsp;the surface, you can think of each Thread like a decentralized &nbsp;database, shared between specific participants. We built Threads into &nbsp;the fabric of Textile (see what we did there 😉) because group members &nbsp;need a record of who shared what photo, and when. But, once we created &nbsp;Threads, we realized just how powerful a concept this was — for those &nbsp;familiar with mobile app development, think <a href="https://realm.io/">Realm</a> or <a href="https://firebase.google.com/">Firebase</a> but <em>without</em> the centralized server.</p> <p>To &nbsp;really understand what Threads brings to the table, you really need to &nbsp;understand Threads themselves. So let’s dig a bit deeper into how &nbsp;Textile conceptualizes and implements Threads, and how that helps keep &nbsp;your photos (and likes and comments, etc) safe and secure on the &nbsp;decentralized web. We’ll start by highlighting the specific requirements &nbsp;we had when developing Threads, and then break down each of these &nbsp;requirements into the specific solutions that we came up with. Along the &nbsp;way, our CTO Sander Pick will highlight how those various solutions &nbsp;came about, and why we think our approach is in the best interest of our &nbsp;users.</p> <h3>The experience</h3> <p><a href="https://textile.photos/">Textile Photos</a> &nbsp;allows small, decentralized, private groups to share photos, send &nbsp;messages, and engage with each other. That’s the experience, so it has &nbsp;to ‘just work’.</p> <p><center><img src="https://cdn-images-1.medium.com/max/800/1*xEE3Pp-LdU_r8qsPvMVS8w.gif" width="400" height="600"/></center></p> <h3>Requirements</h3> <p>To drive the Textile user experience, we identified five key features needed in the sharing protocols:</p> <ol> <li><strong>A mechanism to share and receive state updates within a group of n users</strong><br> To &nbsp;enable photo sharing (and other common interactions such as likes and &nbsp;comments) among a group of friends and/or colleagues, some concept of a <em>shared state</em> is required.</li> <li><strong>A way to ensure the shared state stays resilient to peers dropping out or latency issues</strong><br> Since &nbsp;we’re operating in a mobile environment, we have to expect peers to &nbsp;continually drop ‘offline’ due to coverage issues, app back-grounding, &nbsp;battery optimizations, and a whole slew of other reasons for a mobile &nbsp;device to be cut off from a network.</li> <li><strong>A way to avoid state conflicts with other members of the group</strong><br> On &nbsp;top of the requirements above, when peers do come back online, we don’t &nbsp;want any state changes that were made by other members of the group &nbsp;while they were disconnected from the network to conflict with their own &nbsp;local changes.</li> <li><strong>A mechanism to recover the full state from the network as a whole</strong><br> Another &nbsp;important consideration in the mobile world is that the number of users &nbsp;(out of n) that are online at any given time is generally unknown, and &nbsp;quite possibly zero. To reiterate, we want a decentralized shared state, &nbsp;but it has to work <em>even when you are the only member online</em>. &nbsp;This means we have to assume the full group state may not ever be &nbsp;directly accessible (i.e., downloadable) from a single group member. &nbsp;This is in contrast to something like <a href="https://en.wikipedia.org/wiki/Bitcoin">Bitcoin</a>, where new nodes are able to <a href="https://bitcoin.org/en/developer-guide#initial-block-download">download the full blockchain</a> from any connected peer.</li> <li><strong>Way to link updates via their content, rather than where they are stored</strong><br> Since we are building on top of the IPFS network, and would like to eventually support a <a href="https://filecoin.io/">Filecoin</a>-based &nbsp;future in which users can select from a multitude of decentralized &nbsp;storage providers, Threads need to embrace content addressing, rather &nbsp;than location addressing. This makes it easy to grow and change the &nbsp;underlying network, without affecting data access and sharing.</li> </ol> <p>With these requirements in mind, let’s break down our solutions into their individual components…</p> <h3>Solutions</h3> <h4>1. Handling Updates — use a peer-to-peer network with structured updates</h4> <p>First things first: <em>how do we handle state updates between a set of distributed peers?</em> This is mostly about <a href="https://en.wikipedia.org/wiki/Peer-to-peer">peer-to-peer (p2p) networking</a>. &nbsp;And when it comes to communicating between heterogeneous network &nbsp;devices (computers, phones, IoT devices, etc), we actually need many <a href="https://en.wikipedia.org/wiki/Lists_of_network_protocols">different types of network protocols</a>. &nbsp;That way, no matter what type of device we are talking about — be it a &nbsp;phone, desktop computer, browser, or Internet-enabled fridge — it is &nbsp;able to communicate with other devices located in the same room, or on &nbsp;the other side of the planet.</p> <p>At Textile, we use the super amazing <a href="https://libp2p.io/">libp2p</a> &nbsp;library for our networking needs. Libp2p is a networking stack and &nbsp;library (you might have heard it called a protocol suite) modularized &nbsp;out of the <a href="https://ipfs.io/">IPFS project</a>, &nbsp;and bundled separately for other tools to use. Essentially, libp2p does &nbsp;all the heavy network lifting so that we can focus on our core task: &nbsp;exchanging updates between communicating peers.</p> <p><center><img src="https://cdn-images-1.medium.com/max/800/1*0iCOZW6HrpdQ0izG5O9ciw.png" width="800" height="316"/><em>Decentralized, peer-to-peer networks are radically different types of communication networks.</em></center></p> <blockquote>Libp2p &nbsp;was a pretty natural choice for us. The stack includes all the crypto &nbsp;and networking protocols we need to deliver messages to group members, &nbsp;and the libp2p developer community is super responsive and excited about &nbsp;the power of p2p interactions. Easy choice.</blockquote> <p>The &nbsp;other really nice thing about using the libp2p library is it comes &nbsp;packed with many useful cryptography tools and functions, keeping &nbsp;communications secure. For instance, all p2p communications over the &nbsp;Textile network use the <a href="https://github.com/libp2p/go-libp2p-secio">secio</a> <a href="https://github.com/libp2p/go-stream-security">stream security transport</a>. &nbsp;This way, all connections use secure sessions provided by libp2p/secio &nbsp;to encrypt all traffic, whereby a TLS-like handshake is used to setup &nbsp;the initial communication channel.</p> <p>Like many IPFS-based projects, Textile uses <a href="https://developers.google.com/protocol-buffers/">Protocol Buffers</a> &nbsp;for over-the-wire communication, and advanced cryptographic algorithms &nbsp;to secure those messages. Essentially, each update to the shared group &nbsp;state is just an encrypted Profobuf message with two parts: a header &nbsp;with author and date info, and a body with the type-specific data. These &nbsp;pieces are sent in their own inner-’envelope’ which contains a link to &nbsp;the encrypted message and the Thread ID. This inner-envelope is then &nbsp;signed by the sender and placed into the wire ‘envelope’ along with it’s &nbsp;signature. You can read more about some of the cryptographic tools &nbsp;Textile uses in <a href="https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14">this previous article</a>. You can also check out how we structure our <a href="https://github.com/textileio/textile-go/blob/master/pb/protos/thread.proto">Protobuf messages</a>, learn a bit more about <a href="https://github.com/auditdrivencrypto/secure-channel/blob/master/prior-art.md#ipfss-secure-channel">how secio works</a>, plus check out some <a href="https://github.com/textileio/textile-go/commit/05a269cd5dd72da224c4d2c472abad00a078d4ca">recent updates</a> to message encryption while you’re at it.</p> <h4>2. Network Resilience — support offline messaging so peers can come and&nbsp;go</h4> <p>If you are at all familiar with libp2p, then you might be thinking <em>“ah libp2p has a pubsub layer that would be perfect for exchanging updates to a group of connecting peers”</em>. &nbsp;And while you’d certainly be right, there are a few key limitations &nbsp;that makes using pubsub for something like Textile Photos pretty &nbsp;cumbersome. On top of this, while pubsub is super nice for things like &nbsp;chat rooms or distributed services, it is a ‘fire-and-forget’ messaging &nbsp;protocol, meaning that once a peer publishes a message, it is up to its &nbsp;peers to ensure they are listening for the right message at the right &nbsp;time. To circumvent this, some pubsub systems introduce message echoing, &nbsp;to ensure a message stays in the system long enough to be picked up by &nbsp;the peers who might need it. However, this can lead to really noisy &nbsp;network traffic, and is really just a band-aid over a larger issue.</p> <blockquote>Our &nbsp;initial POC involved pubsub and always-online room echoers… not &nbsp;scalable or particularly decentralized. A real solution to distributing &nbsp;state has to involve direct messaging with an offline mechanism.</blockquote> <p>So this starts to get at our second requirement, that <em>the shared state stays resilient to peers dropping out</em>. &nbsp;We need to assume peers might not be around to receive important &nbsp;messages in ‘real-time’, which is a common problem with p2p systems. &nbsp;Right now, Textile addresses this problem by enabling what you might &nbsp;call <em>offline messaging</em>. Since we’re <a href="https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14#ea20">already using IPFS for data storage and communication</a>, &nbsp;we wanted to take advantage of some of the core technologies driving &nbsp;IPFS. In particular, we (currently) use a special fork of the <a href="https://en.wikipedia.org/wiki/Kademlia">Kademlia</a>-based <a href="https://en.wikipedia.org/wiki/Distributed_hash_table">distributed hash table</a> (DHT) <a href="https://github.com/libp2p/go-libp2p-kad-dht">used by IPFS, </a>that allows us to post messages for a peer directly in the DHT. For those unfamiliar with DHTs, they are a <a href="https://en.wikipedia.org/wiki/Hash_table">hash table</a> &nbsp;where the data is spread across a network of nodes or peers. And these &nbsp;peers are all coordinated to enable efficient access and lookup between &nbsp;nodes in a decentralized way. You can read more about this kind of stuff &nbsp;in our previous article about <a href="https://medium.com/textileio/swapping-bits-and-distributing-hashes-on-the-decentralized-web-5da98a3507">how IPFS peers find, request, and retrieve content (and each other) on the decentralized web</a>. &nbsp;So, when a peer we want to communicate with is offline, rather than &nbsp;blindly sending them a message that will never be received, we post a &nbsp;message to Textile’s DHT, and they can then retrieve that message the &nbsp;next time they come online again. Conceptually simple, and works pretty &nbsp;well in practice.</p> <p><center><img src="https://cdn-images-1.medium.com/max/800/1*JsXZAveyMui2izg_-FMh0g.png" width="800" height="485"/><em>p2p &nbsp;network with custom Textile DHT overlay. Peers post (key, value) &nbsp;messages (value) with a key specific to their intended recipient, and &nbsp;this key is broadcast and available to entire network; though only the &nbsp;intended recipient is able to decrypt the actual message content. Based &nbsp;on Figure 1–2 from </em><a href="https://www.researchgate.net/figure/2-Overlay-and-underlay-view-of-Distributed-Hash-Tables-11_fig2_304495476"><em>this&nbsp;thesis</em></a><em>.</em></center></p> <p>There &nbsp;are still some issues with our current approach, including that it is &nbsp;difficult/impossible to remove messages from the DHT manually. Indeed, &nbsp;it can start to get a bit messy when left-over offline messages have to &nbsp;be retrieved each time a peer comes back online… imagine a peer that &nbsp;goes in and out of service frequently, this could lead to a lot of &nbsp;network traffic and wasted CPU cycles. So, we’ve <a href="https://github.com/libp2p/notes/issues/2#issuecomment-433729343">implemented an alternative</a> &nbsp;to this DHT-based offline messaging system that does not suffer from &nbsp;these limitations (and also allows us to participate in the public IPFS &nbsp;network), while still remaining decentralized and scalable in the &nbsp;long-term. This new approach should be released soon, after more testing &nbsp;and evaluation. You can follow along with this progress as part of the <a href="https://github.com/textileio/textile-go/projects/2">move towards a Cafe-based setup</a> (see also <strong>What’s Next</strong>).</p> <h4>3 &amp; 4. Avoiding Conflicts &amp; State Recovery —use a CRDT to keep an immutable history across&nbsp;peers</h4> <p>Ok, so our next requirement and its associated solution have received a <a href="https://github.com/ipfs/research-CRDT/">great deal of research and development attention over the years</a>. The question of “<em>how to avoid state conflicts with other members of a group?”</em> &nbsp;comes up when working collaboratively on documents, updating shared &nbsp;databases, etc. For the purposes of updating a shared Thread of photos, &nbsp;it turns out that an <a href="https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type">operation-based CRDT</a> &nbsp;that supports append-only operations is pretty much all you need to get &nbsp;going. You can think of Textile’s CRDT (which shares some ideas with <a href="https://github.com/orbitdb/ipfs-log">ipfs-log</a>) &nbsp;setup as an immutable, append-only tree that can be used to model a &nbsp;mutable, shared state between peers. Every entry in the tree is saved on &nbsp;IPFS, and each points to a hash of previous entry(ies) forming a graph. &nbsp;These trees can be <a href="https://www.atlassian.com/git/tutorials/using-branches/git-merge">3-way and fast-forward merged</a>.</p> <p>Speaking &nbsp;of forks and joins, for those familiar with git and other similar &nbsp;system, you might be thinking this sounds a lot like a <a href="https://blog.thoughtram.io/git/2014/11/18/the-anatomy-of-a-git-commit.html#meet-the-tree-object">git hash tree</a>, <a href="https://twitter.com/Textile01/status/1004436869734543360">Merkle DAG</a>, or even a <a href="https://en.wikipedia.org/wiki/Blockchain">blockchain</a>. &nbsp;And you’d be right! The concepts are very similar, and this buys us &nbsp;some really nice properties for building and maintaining a shared state. &nbsp;By modeling our shared Thread state in this way, we benefit from tried &nbsp;and tested methods for allowing a peer to incorporate other peers’ &nbsp;updates into their state while maintaining history (via fast-forwards &nbsp;and three-way merging for example).</p> <blockquote>At the end of the day, a Thread is just a git-like hash tree of updates with a deterministic merge policy. Simple.</blockquote> <p>So &nbsp;what does this look like in practice? Currently — because things might &nbsp;change as we make improvements to the underlying implementation — each &nbsp;Thread in Textile Photos is essentially a chain of updates, where each &nbsp;update represents some specific action or event. For instance, when you &nbsp;create a new Thread, under-the-hood you are actually creating a <code>JOIN</code> update on a new Thread chain. Similarly, when you update the Thread via a new photo (<code>DATA</code> update), comment, or like (<code>ANNOTATION</code> update), you’re actually updating that Thread chain. After each modification, the <code>HEAD</code> of the Thread will point to the latest update.</p> <p>Building on top of these ideas, we also have concepts such as an <code>INVITE</code>, which points a new peer to a given point on the Thread chain, or a <code>MERGE</code>, which happens when the current <code>HEAD</code> &nbsp;is not contained in an incoming update’s parent list for some reason &nbsp;(maybe the peer doesn’t know about it because they were offline). If two &nbsp;peers are merging the <em>same sub trees</em>, &nbsp;all they need to do to ensure the update resolves to the same hash is &nbsp;a) include the same date b) exclude author info. To get the same date, &nbsp;they both follow a rule: choose the latest of the parents for the date &nbsp;(in practice they add a little bit extra on to keep it ahead of both &nbsp;parents).</p> <p>To give you a better idea of what exactly we’re talking about, consider the following set of operations: <code>User A</code> creates a new Thread, and adds a Photo. They then externally invite <code>User B</code> (sent via some other <em>secure</em> communication channel), who eventually joins the Thread. But before <code>User B</code> is able to join the Thread, <code>User A</code> adds another Photo, moving the Thread’s <code>HEAD</code> forward. By the time <code>User B</code> joins the Thread, they’d end up with a Thread sequence that looks something like this:</p> <p><center><img src="https://cdn-images-1.medium.com/max/800/1*h9eNwOauuT7mMeS4o-yGbw.png" width="800" height="478"/><em>Thread &nbsp;join example. Solid arrows point towards the ‘parent’ of a given &nbsp;update, over-the-wire communications are indicated with a 📶-style &nbsp;arrow, and messages that are rebroadcast (e.g., via the welcome message) &nbsp;are indicated with a dashed arrow. Similarly, merges point to both &nbsp;their parent&nbsp;updates.</em></center></p> <p>Here, &nbsp;we see the merge happening at the end of the sequence because the &nbsp;bottom peer is joining via an external invite that is no longer <code>HEAD</code>&nbsp;, forcing them to merge the most recent <code>DATA</code> update with their own <code>JOIN</code> update. But since merge results are deterministic (given the same parents), both peers create the <code>MERGE</code> update locally, and do not broadcast them to avoid trading merges back and forth.</p> <p>A more complete sequence is given in the following figure. Suppose <code>User A</code> &nbsp;goes ‘offline’ (e.g., their phone goes to sleep, they shut down the &nbsp;app, they lose their data connection, etc), and in the mean time, both<code> Users A</code> and <code>B</code> update the Thread, with <code>User A</code> adding an <code>ANNOTATION</code> update, and <code>User B</code> adding a new Photo (<code>DATA</code> update). Now, when <code>User A</code> comes back online, there is a conflict, and both Users create a <code>MERGE</code> update to remedy this. A <code>MERGE</code> update has two parents, in this case, the <code>DATA</code> and <code>ANNOTATION</code> update from the different users. As always, the <code>HEAD</code> continues to point to the latest update (which in the example below eventually becomes an <code>ANNOTATION</code> from <code>User B</code>). Once both peers are online again, the more straightforward update and transmit mode of operation can continue.</p> <p><center><img src="https://cdn-images-1.medium.com/max/800/1*3EuEtFHqUtALTczIeZ8_eg.png" width="800" height="473"/><em>More &nbsp;complex Thread interaction where one or more peers are temporarily &nbsp;offline. Note that an external invite is the same as a normal invite, &nbsp;but the invite details are encrypted with a single use key, which is &nbsp;sharable with the invite update location.</em></center></p> <p>The &nbsp;same properties that make hash trees or blockchains useful for &nbsp;developing a shared, consistent (consensus-driven) state, also makes it &nbsp;possible to address our fourth requirement: <em>the ability to recover the full state from the network as a whole</em>. &nbsp;Because each Thread update references its parent(s), given a single &nbsp;point on the Thread chain, we can trace back all the way to the &nbsp;beginning of the Thread. For example, at any point along the sequence in &nbsp;the above figures, a peer can trace back the history of the Thread, as &nbsp;indicated by the solid arrows. This works particularly nicely when a &nbsp;peer <code>JOIN</code>s a thread, even at a point prior to the current <code>HEAD</code>. They can simply <code>JOIN</code>, and any existing Thread member can send them the latest <code>HEAD</code> &nbsp;(even via offline messages if needed). From here, they can explore the &nbsp;entire history of the Thread with ease. This is all really similar to &nbsp;git commit speak, in which one only needs to know about a single commit &nbsp;to be able to trace back the entire history of a code project; it’s also &nbsp;essentially how blockchains work.</p> <h4>5. Content Addressing — store everything on IPFS and get ready to&nbsp;scale</h4> <p>As &nbsp;we alluded to earlier, each update to a Thread is backed by an IPFS CID &nbsp;hash (i.e., they are content addressable chunks of data on IPFS). This &nbsp;means <em>where</em> the data is stored &nbsp;is no longer relevant… IPFS will find it on the network via it’s hash. &nbsp;This helps us address our fifth requirement, that we have a <em>way to link updates via their content, rather than where they are stored</em>. We’ve covered this topic a lot <a href="https://medium.com/textileio">in the past</a>, but for the uninitiated, the next paragraph provides a summary of how content addressing on IPFS works (pulled from <a href="https://medium.com/textileio/enabling-the-distributed-web-abf7ab33b638">this previous article</a>).</p> <p>Rather than referencing a file or chunk of data by its location (think HTTP), we reference it via its <a href="https://en.wikipedia.org/wiki/Fingerprint_%28computing%29">fingerprint</a>. In IPFS and other such systems, this means identifying content by its cryptographic hash, or even better, a <a href="https://github.com/ipld/cid"><em>self-describing </em>content-addressed identifier</a> (<a href="https://github.com/multiformats/multihash">multihash</a>). A cryptographic hash is a (relatively) short alphanumeric string that’s calculated by running your content through a <a href="https://en.wikipedia.org/wiki/Cryptographic_hash_function">cryptographic hash function</a> (like <a href="https://en.wikipedia.org/wiki/SHA-3">SHA</a>). For example, when the (unencrypted) <a href="https://ipfs.io/ipfs/QmcNzGMQ1hUzSnqM2aH1Um8KWLRC4Rd9TyY4kFuYZXWXaF/Textile_Icon_500x500.png">Textile logo</a> is added to IPFS, its multihash ends up being <code>QmbgGgWW3vH7v9FDxVCzcouKGChqGEjtf6YLDUgSHnk5J2</code>. This ‘hash’ is actually the CID (Content IDentifier) for that file, computed from the <em>raw data </em>within that PNG. It is guaranteed to be cryptographically unique to the contents of <em>that </em>file, and that file only. If we change that file by even one bit, the hash will become something completely different.</p> <p>Now, &nbsp;when we want to access a file over IPFS (like the above logo), we can &nbsp;simply ask the IPFS network for the file with that exact CID, the &nbsp;network will find the peers that have the data (using a DHT), retrieve &nbsp;it, and verify (using the CID) that it’s the correct file. What this &nbsp;means is we can technically get the file from <em>multiple </em>places &nbsp;because as long as the file matches the hash, we know we’re getting the &nbsp;right data. Which brings us to the solution to our final requirement… &nbsp;use IPFS! For now, Textile is maintaining a network of large, &nbsp;homogeneous, volunteer nodes (we call them <a href="https://github.com/textileio/textile-go">Cafe</a>s) &nbsp;to ‘pin’ and store content on IPFS. It is important to note here that &nbsp;the other nodes doing the pinning are the same as the nodes on your &nbsp;phone — Textile Nodes that offer a pinning service to other peers. Soon, &nbsp;we’ll allow users to elect their own Cafe nodes, add even add &nbsp;additional nodes for redundancy. All this could eventually be driven by &nbsp;Filecoin for even greater scalablility and flexibility.</p> <h3>What’s Next?</h3> <p>So &nbsp;there you have it. Five solutions to five requirements for seamless, &nbsp;secure, decentralized photo sharing and backup. Easy 😉. And at a &nbsp;conceptual level, the Textile Thread protocol <em>is</em> &nbsp;relatively simple: blocks of operations chained together to produce a &nbsp;beautiful Thread of photos. But there’s a lot of complexity going on &nbsp;under-the-hood that has required a lot of experimentation, testing, and &nbsp;limit pushing, especially on mobile. And our journey isn’t over yet.</p> <p><a href="https://twitter.com/Textile01/lists/textile-team/members">The Textile team</a> &nbsp;is still hard at work iterating, updating, and improving upon what we &nbsp;already have working. For example, we’ll soon to moving to a new offline &nbsp;messaging system that allow us to drop the custom DHT fork, and move &nbsp;back to the public IPFS network. On top of this, our <a href="https://github.com/textileio/textile-go/projects/2">move to more powerful backup</a> &nbsp;and recovery capabilities has us taking new approaches to security, &nbsp;profile management, offline interactions, and much much more. On top of &nbsp;these changes, the team is actively working to modularize the Threads &nbsp;concept and code into its own stand-alone package, which should provide &nbsp;developers with something akin to a Realm and/or Firebase layer for &nbsp;decentralized mobile applications!</p> <p>If you are interested in learning more about this stuff, reach out over <a href="https://twitter.com/Textile01">Twitter</a> or <a href="https://slack.textile.io/">Slack</a>, &nbsp;or pull us aside the next time you see us at a conference or event. We’re happy to provide background, thoughts, and opinions on how we &nbsp;think the future of decentralized apps will play out. In the mean time, &nbsp;don’t forget to check out our <a href="https://github.com/textileio">GitHub repos</a> &nbsp;for code and PRs that showcase our current and old implementations. We &nbsp;try to make sure all our development happens out in the open, so you can &nbsp;see things as they develop. Additionally, if you haven’t already, <a href="https://www.textile.photos/#cta">don’t miss out on signing up for our waitlist</a>, where you can get early access to Textile Photos, the beautiful interface to Textile’s Threads.</p> </html>
titleTextile Threads whitepaper… just kidding… a deeper look at the tech behind Textile’s Threads protocol
authorsanderpick
permlinktextile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol
json metadata{"tags":["mobile","p2p","crypto","blockchain","decentralization"],"image":["https://cdn-images-1.medium.com/max/800/1*beyK889WqmNEatN55oBAnA.png","https://cdn-images-1.medium.com/max/800/1*xEE3Pp-LdU_r8qsPvMVS8w.gif","https://cdn-images-1.medium.com/max/800/1*0iCOZW6HrpdQ0izG5O9ciw.png","https://cdn-images-1.medium.com/max/800/1*JsXZAveyMui2izg_-FMh0g.png","https://cdn-images-1.medium.com/max/800/1*h9eNwOauuT7mMeS4o-yGbw.png","https://cdn-images-1.medium.com/max/800/1*3EuEtFHqUtALTczIeZ8_eg.png"],"links":["https://medium.com/@carsonfarmer","https://medium.com/@sanderpick","https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14","https://realm.io/","https://firebase.google.com/","https://textile.photos/","https://en.wikipedia.org/wiki/Bitcoin","https://bitcoin.org/en/developer-guide#initial-block-download","https://filecoin.io/","https://en.wikipedia.org/wiki/Peer-to-peer","https://en.wikipedia.org/wiki/Lists_of_network_protocols","https://libp2p.io/","https://ipfs.io/","https://github.com/libp2p/go-libp2p-secio","https://github.com/libp2p/go-stream-security","https://developers.google.com/protocol-buffers/","https://github.com/textileio/textile-go/blob/master/pb/protos/thread.proto","https://github.com/auditdrivencrypto/secure-channel/blob/master/prior-art.md#ipfss-secure-channel","https://github.com/textileio/textile-go/commit/05a269cd5dd72da224c4d2c472abad00a078d4ca","https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14#ea20","https://en.wikipedia.org/wiki/Kademlia","https://en.wikipedia.org/wiki/Distributed_hash_table","https://github.com/libp2p/go-libp2p-kad-dht","https://en.wikipedia.org/wiki/Hash_table","https://medium.com/textileio/swapping-bits-and-distributing-hashes-on-the-decentralized-web-5da98a3507","https://www.researchgate.net/figure/2-Overlay-and-underlay-view-of-Distributed-Hash-Tables-11_fig2_304495476","https://github.com/libp2p/notes/issues/2#issuecomment-433729343","https://github.com/textileio/textile-go/projects/2","https://github.com/ipfs/research-CRDT/","https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type","https://github.com/orbitdb/ipfs-log","https://www.atlassian.com/git/tutorials/using-branches/git-merge","https://blog.thoughtram.io/git/2014/11/18/the-anatomy-of-a-git-commit.html#meet-the-tree-object","https://twitter.com/Textile01/status/1004436869734543360","https://en.wikipedia.org/wiki/Blockchain","https://medium.com/textileio","https://medium.com/textileio/enabling-the-distributed-web-abf7ab33b638","https://en.wikipedia.org/wiki/Fingerprint_%28computing%29","https://github.com/ipld/cid","https://github.com/multiformats/multihash","https://en.wikipedia.org/wiki/Cryptographic_hash_function","https://en.wikipedia.org/wiki/SHA-3","https://ipfs.io/ipfs/QmcNzGMQ1hUzSnqM2aH1Um8KWLRC4Rd9TyY4kFuYZXWXaF/Textile_Icon_500x500.png","https://github.com/textileio/textile-go","https://twitter.com/Textile01/lists/textile-team/members","https://twitter.com/Textile01","https://slack.textile.io/","https://github.com/textileio","https://www.textile.photos/#cta"],"app":"steemit/0.1","format":"html"}
parent author
parent permlinkmobile
Transaction InfoBlock #27323219/Trx 1b0fac506cdfb108f722b2e1c911db0abc02ff43
View Raw JSON Data
{
  "op": [
    "comment",
    {
      "body": "<html>\n<p><em>Written by </em><a href=\"https://medium.com/@carsonfarmer\"><em>Carson Farmer</em></a><em> &amp; </em><a href=\"https://medium.com/@sanderpick\"><em>Sander Pick</em></a></p>\n<p><center><img src=\"https://cdn-images-1.medium.com/max/800/1*beyK889WqmNEatN55oBAnA.png\" width=\"800\" height=\"565\"/><em>Download the app, take a picture, share!</em></center></p>\n<p>Recently, &nbsp;we’ve started writing more about the technologies underlying Textile &nbsp;Photos that help keep your photos (and likes and comments, etc) safe and &nbsp;secure on the decentralized web. In our previous post, we talked about <a href=\"https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14\">the encryption process Textile Photos</a>, &nbsp;with a focus how Textile delivers end-to-end encrypted photo sharing. &nbsp;Today’s post is a follow-up (though it should also be sufficiently &nbsp;detailed to stand on its own), this time highlighting how Textile &nbsp;coordinates private photo sharing among groups of users, a feature we &nbsp;call <em>Threads</em>.</p>\n<p><strong>Why we built it</strong><br>\nWe &nbsp;designed Threads to allow groups of users to share photos securely and &nbsp;privately, without any centralized, authoritative database. We also made &nbsp;sure it all works well offline, that its possible to recover lost data, &nbsp;and that its easy to add new members.</p>\n<p><strong>What makes Threads different</strong><br>\nThreads &nbsp;allow private groups to post photos and interact over a decentralized &nbsp;network, maintaining complete control over their own content. Textile &nbsp;operates in a completely zero-knowledge framework. Private by design.</p>\n<p><strong>Why Threads are exciting</strong><br>\nBecause &nbsp;photos are just the first step. Today, Threads allow users to share a &nbsp;photo with other Thread in a secure, decentralized way. Threads can &nbsp;facilitate secure sharing, coordination, and storage of <em>many</em> types of data over a decentralized network. Upgradable by design.</p>\n<p>On &nbsp;the surface, you can think of each Thread like a decentralized &nbsp;database, shared between specific participants. We built Threads into &nbsp;the fabric of Textile (see what we did there 😉) because group members &nbsp;need a record of who shared what photo, and when. But, once we created &nbsp;Threads, we realized just how powerful a concept this was — for those &nbsp;familiar with mobile app development, think <a href=\"https://realm.io/\">Realm</a> or <a href=\"https://firebase.google.com/\">Firebase</a> but <em>without</em> the centralized server.</p>\n<p>To &nbsp;really understand what Threads brings to the table, you really need to &nbsp;understand Threads themselves. So let’s dig a bit deeper into how &nbsp;Textile conceptualizes and implements Threads, and how that helps keep &nbsp;your photos (and likes and comments, etc) safe and secure on the &nbsp;decentralized web. We’ll start by highlighting the specific requirements &nbsp;we had when developing Threads, and then break down each of these &nbsp;requirements into the specific solutions that we came up with. Along the &nbsp;way, our CTO Sander Pick will highlight how those various solutions &nbsp;came about, and why we think our approach is in the best interest of our &nbsp;users.</p>\n<h3>The experience</h3>\n<p><a href=\"https://textile.photos/\">Textile Photos</a> &nbsp;allows small, decentralized, private groups to share photos, send &nbsp;messages, and engage with each other. That’s the experience, so it has &nbsp;to ‘just work’.</p>\n<p><center><img src=\"https://cdn-images-1.medium.com/max/800/1*xEE3Pp-LdU_r8qsPvMVS8w.gif\" width=\"400\" height=\"600\"/></center></p>\n<h3>Requirements</h3>\n<p>To drive the Textile user experience, we identified five key features needed in the sharing protocols:</p>\n<ol>\n  <li><strong>A mechanism to share and receive state updates within a group of n users</strong><br>\nTo &nbsp;enable photo sharing (and other common interactions such as likes and &nbsp;comments) among a group of friends and/or colleagues, some concept of a <em>shared state</em> is required.</li>\n  <li><strong>A way to ensure the shared state stays resilient to peers dropping out or latency issues</strong><br>\nSince &nbsp;we’re operating in a mobile environment, we have to expect peers to &nbsp;continually drop ‘offline’ due to coverage issues, app back-grounding, &nbsp;battery optimizations, and a whole slew of other reasons for a mobile &nbsp;device to be cut off from a network.</li>\n  <li><strong>A way to avoid state conflicts with other members of the group</strong><br>\nOn &nbsp;top of the requirements above, when peers do come back online, we don’t &nbsp;want any state changes that were made by other members of the group &nbsp;while they were disconnected from the network to conflict with their own &nbsp;local changes.</li>\n  <li><strong>A mechanism to recover the full state from the network as a whole</strong><br>\nAnother &nbsp;important consideration in the mobile world is that the number of users &nbsp;(out of n) that are online at any given time is generally unknown, and &nbsp;quite possibly zero. To reiterate, we want a decentralized shared state, &nbsp;but it has to work <em>even when you are the only member online</em>. &nbsp;This means we have to assume the full group state may not ever be &nbsp;directly accessible (i.e., downloadable) from a single group member. &nbsp;This is in contrast to something like <a href=\"https://en.wikipedia.org/wiki/Bitcoin\">Bitcoin</a>, where new nodes are able to <a href=\"https://bitcoin.org/en/developer-guide#initial-block-download\">download the full blockchain</a> from any connected peer.</li>\n  <li><strong>Way to link updates via their content, rather than where they are stored</strong><br>\nSince we are building on top of the IPFS network, and would like to eventually support a <a href=\"https://filecoin.io/\">Filecoin</a>-based &nbsp;future in which users can select from a multitude of decentralized &nbsp;storage providers, Threads need to embrace content addressing, rather &nbsp;than location addressing. This makes it easy to grow and change the &nbsp;underlying network, without affecting data access and sharing.</li>\n</ol>\n<p>With these requirements in mind, let’s break down our solutions into their individual components…</p>\n<h3>Solutions</h3>\n<h4>1. Handling Updates — use a peer-to-peer network with structured updates</h4>\n<p>First things first: <em>how do we handle state updates between a set of distributed peers?</em> This is mostly about <a href=\"https://en.wikipedia.org/wiki/Peer-to-peer\">peer-to-peer (p2p) networking</a>. &nbsp;And when it comes to communicating between heterogeneous network &nbsp;devices (computers, phones, IoT devices, etc), we actually need many <a href=\"https://en.wikipedia.org/wiki/Lists_of_network_protocols\">different types of network protocols</a>. &nbsp;That way, no matter what type of device we are talking about — be it a &nbsp;phone, desktop computer, browser, or Internet-enabled fridge — it is &nbsp;able to communicate with other devices located in the same room, or on &nbsp;the other side of the planet.</p>\n<p>At Textile, we use the super amazing <a href=\"https://libp2p.io/\">libp2p</a> &nbsp;library for our networking needs. Libp2p is a networking stack and &nbsp;library (you might have heard it called a protocol suite) modularized &nbsp;out of the <a href=\"https://ipfs.io/\">IPFS project</a>, &nbsp;and bundled separately for other tools to use. Essentially, libp2p does &nbsp;all the heavy network lifting so that we can focus on our core task: &nbsp;exchanging updates between communicating peers.</p>\n<p><center><img src=\"https://cdn-images-1.medium.com/max/800/1*0iCOZW6HrpdQ0izG5O9ciw.png\" width=\"800\" height=\"316\"/><em>Decentralized, peer-to-peer networks are radically different types of communication networks.</em></center></p>\n<blockquote>Libp2p &nbsp;was a pretty natural choice for us. The stack includes all the crypto &nbsp;and networking protocols we need to deliver messages to group members, &nbsp;and the libp2p developer community is super responsive and excited about &nbsp;the power of p2p interactions. Easy choice.</blockquote>\n<p>The &nbsp;other really nice thing about using the libp2p library is it comes &nbsp;packed with many useful cryptography tools and functions, keeping &nbsp;communications secure. For instance, all p2p communications over the &nbsp;Textile network use the <a href=\"https://github.com/libp2p/go-libp2p-secio\">secio</a> <a href=\"https://github.com/libp2p/go-stream-security\">stream security transport</a>. &nbsp;This way, all connections use secure sessions provided by libp2p/secio &nbsp;to encrypt all traffic, whereby a TLS-like handshake is used to setup &nbsp;the initial communication channel.</p>\n<p>Like many IPFS-based projects, Textile uses <a href=\"https://developers.google.com/protocol-buffers/\">Protocol Buffers</a> &nbsp;for over-the-wire communication, and advanced cryptographic algorithms &nbsp;to secure those messages. Essentially, each update to the shared group &nbsp;state is just an encrypted Profobuf message with two parts: a header &nbsp;with author and date info, and a body with the type-specific data. These &nbsp;pieces are sent in their own inner-’envelope’ which contains a link to &nbsp;the encrypted message and the Thread ID. This inner-envelope is then &nbsp;signed by the sender and placed into the wire ‘envelope’ along with it’s &nbsp;signature. You can read more about some of the cryptographic tools &nbsp;Textile uses in <a href=\"https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14\">this previous article</a>. You can also check out how we structure our <a href=\"https://github.com/textileio/textile-go/blob/master/pb/protos/thread.proto\">Protobuf messages</a>, learn a bit more about <a href=\"https://github.com/auditdrivencrypto/secure-channel/blob/master/prior-art.md#ipfss-secure-channel\">how secio works</a>, plus check out some <a href=\"https://github.com/textileio/textile-go/commit/05a269cd5dd72da224c4d2c472abad00a078d4ca\">recent updates</a> to message encryption while you’re at it.</p>\n<h4>2. Network Resilience — support offline messaging so peers can come and&nbsp;go</h4>\n<p>If you are at all familiar with libp2p, then you might be thinking <em>“ah libp2p has a pubsub layer that would be perfect for exchanging updates to a group of connecting peers”</em>. &nbsp;And while you’d certainly be right, there are a few key limitations &nbsp;that makes using pubsub for something like Textile Photos pretty &nbsp;cumbersome. On top of this, while pubsub is super nice for things like &nbsp;chat rooms or distributed services, it is a ‘fire-and-forget’ messaging &nbsp;protocol, meaning that once a peer publishes a message, it is up to its &nbsp;peers to ensure they are listening for the right message at the right &nbsp;time. To circumvent this, some pubsub systems introduce message echoing, &nbsp;to ensure a message stays in the system long enough to be picked up by &nbsp;the peers who might need it. However, this can lead to really noisy &nbsp;network traffic, and is really just a band-aid over a larger issue.</p>\n<blockquote>Our &nbsp;initial POC involved pubsub and always-online room echoers… not &nbsp;scalable or particularly decentralized. A real solution to distributing &nbsp;state has to involve direct messaging with an offline mechanism.</blockquote>\n<p>So this starts to get at our second requirement, that <em>the shared state stays resilient to peers dropping out</em>. &nbsp;We need to assume peers might not be around to receive important &nbsp;messages in ‘real-time’, which is a common problem with p2p systems. &nbsp;Right now, Textile addresses this problem by enabling what you might &nbsp;call <em>offline messaging</em>. Since we’re <a href=\"https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14#ea20\">already using IPFS for data storage and communication</a>, &nbsp;we wanted to take advantage of some of the core technologies driving &nbsp;IPFS. In particular, we (currently) use a special fork of the <a href=\"https://en.wikipedia.org/wiki/Kademlia\">Kademlia</a>-based <a href=\"https://en.wikipedia.org/wiki/Distributed_hash_table\">distributed hash table</a> (DHT) <a href=\"https://github.com/libp2p/go-libp2p-kad-dht\">used by IPFS, </a>that allows us to post messages for a peer directly in the DHT. For those unfamiliar with DHTs, they are a <a href=\"https://en.wikipedia.org/wiki/Hash_table\">hash table</a> &nbsp;where the data is spread across a network of nodes or peers. And these &nbsp;peers are all coordinated to enable efficient access and lookup between &nbsp;nodes in a decentralized way. You can read more about this kind of stuff &nbsp;in our previous article about <a href=\"https://medium.com/textileio/swapping-bits-and-distributing-hashes-on-the-decentralized-web-5da98a3507\">how IPFS peers find, request, and retrieve content (and each other) on the decentralized web</a>. &nbsp;So, when a peer we want to communicate with is offline, rather than &nbsp;blindly sending them a message that will never be received, we post a &nbsp;message to Textile’s DHT, and they can then retrieve that message the &nbsp;next time they come online again. Conceptually simple, and works pretty &nbsp;well in practice.</p>\n<p><center><img src=\"https://cdn-images-1.medium.com/max/800/1*JsXZAveyMui2izg_-FMh0g.png\" width=\"800\" height=\"485\"/><em>p2p &nbsp;network with custom Textile DHT overlay. Peers post (key, value) &nbsp;messages (value) with a key specific to their intended recipient, and &nbsp;this key is broadcast and available to entire network; though only the &nbsp;intended recipient is able to decrypt the actual message content. Based &nbsp;on Figure 1–2 from </em><a href=\"https://www.researchgate.net/figure/2-Overlay-and-underlay-view-of-Distributed-Hash-Tables-11_fig2_304495476\"><em>this&nbsp;thesis</em></a><em>.</em></center></p>\n<p>There &nbsp;are still some issues with our current approach, including that it is &nbsp;difficult/impossible to remove messages from the DHT manually. Indeed, &nbsp;it can start to get a bit messy when left-over offline messages have to &nbsp;be retrieved each time a peer comes back online… imagine a peer that &nbsp;goes in and out of service frequently, this could lead to a lot of &nbsp;network traffic and wasted CPU cycles. So, we’ve <a href=\"https://github.com/libp2p/notes/issues/2#issuecomment-433729343\">implemented an alternative</a> &nbsp;to this DHT-based offline messaging system that does not suffer from &nbsp;these limitations (and also allows us to participate in the public IPFS &nbsp;network), while still remaining decentralized and scalable in the &nbsp;long-term. This new approach should be released soon, after more testing &nbsp;and evaluation. You can follow along with this progress as part of the <a href=\"https://github.com/textileio/textile-go/projects/2\">move towards a Cafe-based setup</a> (see also <strong>What’s Next</strong>).</p>\n<h4>3 &amp; 4. Avoiding Conflicts &amp; State Recovery —use a CRDT to keep an immutable history across&nbsp;peers</h4>\n<p>Ok, so our next requirement and its associated solution have received a <a href=\"https://github.com/ipfs/research-CRDT/\">great deal of research and development attention over the years</a>. The question of “<em>how to avoid state conflicts with other members of a group?”</em> &nbsp;comes up when working collaboratively on documents, updating shared &nbsp;databases, etc. For the purposes of updating a shared Thread of photos, &nbsp;it turns out that an <a href=\"https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type\">operation-based CRDT</a> &nbsp;that supports append-only operations is pretty much all you need to get &nbsp;going. You can think of Textile’s CRDT (which shares some ideas with <a href=\"https://github.com/orbitdb/ipfs-log\">ipfs-log</a>) &nbsp;setup as an immutable, append-only tree that can be used to model a &nbsp;mutable, shared state between peers. Every entry in the tree is saved on &nbsp;IPFS, and each points to a hash of previous entry(ies) forming a graph. &nbsp;These trees can be <a href=\"https://www.atlassian.com/git/tutorials/using-branches/git-merge\">3-way and fast-forward merged</a>.</p>\n<p>Speaking &nbsp;of forks and joins, for those familiar with git and other similar &nbsp;system, you might be thinking this sounds a lot like a <a href=\"https://blog.thoughtram.io/git/2014/11/18/the-anatomy-of-a-git-commit.html#meet-the-tree-object\">git hash tree</a>, <a href=\"https://twitter.com/Textile01/status/1004436869734543360\">Merkle DAG</a>, or even a <a href=\"https://en.wikipedia.org/wiki/Blockchain\">blockchain</a>. &nbsp;And you’d be right! The concepts are very similar, and this buys us &nbsp;some really nice properties for building and maintaining a shared state. &nbsp;By modeling our shared Thread state in this way, we benefit from tried &nbsp;and tested methods for allowing a peer to incorporate other peers’ &nbsp;updates into their state while maintaining history (via fast-forwards &nbsp;and three-way merging for example).</p>\n<blockquote>At the end of the day, a Thread is just a git-like hash tree of updates with a deterministic merge policy. Simple.</blockquote>\n<p>So &nbsp;what does this look like in practice? Currently — because things might &nbsp;change as we make improvements to the underlying implementation — each &nbsp;Thread in Textile Photos is essentially a chain of updates, where each &nbsp;update represents some specific action or event. For instance, when you &nbsp;create a new Thread, under-the-hood you are actually creating a <code>JOIN</code> update on a new Thread chain. Similarly, when you update the Thread via a new photo (<code>DATA</code> update), comment, or like (<code>ANNOTATION</code> update), you’re actually updating that Thread chain. After each modification, the <code>HEAD</code> of the Thread will point to the latest update.</p>\n<p>Building on top of these ideas, we also have concepts such as an <code>INVITE</code>, which points a new peer to a given point on the Thread chain, or a <code>MERGE</code>, which happens when the current <code>HEAD</code> &nbsp;is not contained in an incoming update’s parent list for some reason &nbsp;(maybe the peer doesn’t know about it because they were offline). If two &nbsp;peers are merging the <em>same sub trees</em>, &nbsp;all they need to do to ensure the update resolves to the same hash is &nbsp;a) include the same date b) exclude author info. To get the same date, &nbsp;they both follow a rule: choose the latest of the parents for the date &nbsp;(in practice they add a little bit extra on to keep it ahead of both &nbsp;parents).</p>\n<p>To give you a better idea of what exactly we’re talking about, consider the following set of operations: <code>User A</code> creates a new Thread, and adds a Photo. They then externally invite <code>User B</code> (sent via some other <em>secure</em> communication channel), who eventually joins the Thread. But before <code>User B</code> is able to join the Thread, <code>User A</code> adds another Photo, moving the Thread’s <code>HEAD</code> forward. By the time <code>User B</code> joins the Thread, they’d end up with a Thread sequence that looks something like this:</p>\n<p><center><img src=\"https://cdn-images-1.medium.com/max/800/1*h9eNwOauuT7mMeS4o-yGbw.png\" width=\"800\" height=\"478\"/><em>Thread &nbsp;join example. Solid arrows point towards the ‘parent’ of a given &nbsp;update, over-the-wire communications are indicated with a 📶-style &nbsp;arrow, and messages that are rebroadcast (e.g., via the welcome message) &nbsp;are indicated with a dashed arrow. Similarly, merges point to both &nbsp;their parent&nbsp;updates.</em></center></p>\n<p>Here, &nbsp;we see the merge happening at the end of the sequence because the &nbsp;bottom peer is joining via an external invite that is no longer <code>HEAD</code>&nbsp;, forcing them to merge the most recent <code>DATA</code> update with their own <code>JOIN</code> update. But since merge results are deterministic (given the same parents), both peers create the <code>MERGE</code> update locally, and do not broadcast them to avoid trading merges back and forth.</p>\n<p>A more complete sequence is given in the following figure. Suppose <code>User A</code> &nbsp;goes ‘offline’ (e.g., their phone goes to sleep, they shut down the &nbsp;app, they lose their data connection, etc), and in the mean time, both<code> Users A</code> and <code>B</code> update the Thread, with <code>User A</code> adding an <code>ANNOTATION</code> update, and <code>User B</code> adding a new Photo (<code>DATA</code> update). Now, when <code>User A</code> comes back online, there is a conflict, and both Users create a <code>MERGE</code> update to remedy this. A <code>MERGE</code> update has two parents, in this case, the <code>DATA</code> and <code>ANNOTATION</code> update from the different users. As always, the <code>HEAD</code> continues to point to the latest update (which in the example below eventually becomes an <code>ANNOTATION</code> from <code>User B</code>). Once both peers are online again, the more straightforward update and transmit mode of operation can continue.</p>\n<p><center><img src=\"https://cdn-images-1.medium.com/max/800/1*3EuEtFHqUtALTczIeZ8_eg.png\" width=\"800\" height=\"473\"/><em>More &nbsp;complex Thread interaction where one or more peers are temporarily &nbsp;offline. Note that an external invite is the same as a normal invite, &nbsp;but the invite details are encrypted with a single use key, which is &nbsp;sharable with the invite update location.</em></center></p>\n<p>The &nbsp;same properties that make hash trees or blockchains useful for &nbsp;developing a shared, consistent (consensus-driven) state, also makes it &nbsp;possible to address our fourth requirement: <em>the ability to recover the full state from the network as a whole</em>. &nbsp;Because each Thread update references its parent(s), given a single &nbsp;point on the Thread chain, we can trace back all the way to the &nbsp;beginning of the Thread. For example, at any point along the sequence in &nbsp;the above figures, a peer can trace back the history of the Thread, as &nbsp;indicated by the solid arrows. This works particularly nicely when a &nbsp;peer <code>JOIN</code>s a thread, even at a point prior to the current <code>HEAD</code>. They can simply <code>JOIN</code>, and any existing Thread member can send them the latest <code>HEAD</code> &nbsp;(even via offline messages if needed). From here, they can explore the &nbsp;entire history of the Thread with ease. This is all really similar to &nbsp;git commit speak, in which one only needs to know about a single commit &nbsp;to be able to trace back the entire history of a code project; it’s also &nbsp;essentially how blockchains work.</p>\n<h4>5. Content Addressing — store everything on IPFS and get ready to&nbsp;scale</h4>\n<p>As &nbsp;we alluded to earlier, each update to a Thread is backed by an IPFS CID &nbsp;hash (i.e., they are content addressable chunks of data on IPFS). This &nbsp;means <em>where</em> the data is stored &nbsp;is no longer relevant… IPFS will find it on the network via it’s hash. &nbsp;This helps us address our fifth requirement, that we have a <em>way to link updates via their content, rather than where they are stored</em>. We’ve covered this topic a lot <a href=\"https://medium.com/textileio\">in the past</a>, but for the uninitiated, the next paragraph provides a summary of how content addressing on IPFS works (pulled from <a href=\"https://medium.com/textileio/enabling-the-distributed-web-abf7ab33b638\">this previous article</a>).</p>\n<p>Rather than referencing a file or chunk of data by its location (think HTTP), we reference it via its <a href=\"https://en.wikipedia.org/wiki/Fingerprint_%28computing%29\">fingerprint</a>. In IPFS and other such systems, this means identifying content by its cryptographic hash, or even better, a <a href=\"https://github.com/ipld/cid\"><em>self-describing </em>content-addressed identifier</a> (<a href=\"https://github.com/multiformats/multihash\">multihash</a>). A cryptographic hash is a (relatively) short alphanumeric string that’s calculated by running your content through a <a href=\"https://en.wikipedia.org/wiki/Cryptographic_hash_function\">cryptographic hash function</a> (like <a href=\"https://en.wikipedia.org/wiki/SHA-3\">SHA</a>). For example, when the (unencrypted) <a href=\"https://ipfs.io/ipfs/QmcNzGMQ1hUzSnqM2aH1Um8KWLRC4Rd9TyY4kFuYZXWXaF/Textile_Icon_500x500.png\">Textile logo</a> is added to IPFS, its multihash ends up being <code>QmbgGgWW3vH7v9FDxVCzcouKGChqGEjtf6YLDUgSHnk5J2</code>. This ‘hash’ is actually the CID (Content IDentifier) for that file, computed from the <em>raw data </em>within that PNG. It is guaranteed to be cryptographically unique to the contents of <em>that </em>file, and that file only. If we change that file by even one bit, the hash will become something completely different.</p>\n<p>Now, &nbsp;when we want to access a file over IPFS (like the above logo), we can &nbsp;simply ask the IPFS network for the file with that exact CID, the &nbsp;network will find the peers that have the data (using a DHT), retrieve &nbsp;it, and verify (using the CID) that it’s the correct file. What this &nbsp;means is we can technically get the file from <em>multiple </em>places &nbsp;because as long as the file matches the hash, we know we’re getting the &nbsp;right data. Which brings us to the solution to our final requirement… &nbsp;use IPFS! For now, Textile is maintaining a network of large, &nbsp;homogeneous, volunteer nodes (we call them <a href=\"https://github.com/textileio/textile-go\">Cafe</a>s) &nbsp;to ‘pin’ and store content on IPFS. It is important to note here that &nbsp;the other nodes doing the pinning are the same as the nodes on your &nbsp;phone — Textile Nodes that offer a pinning service to other peers. Soon, &nbsp;we’ll allow users to elect their own Cafe nodes, add even add &nbsp;additional nodes for redundancy. All this could eventually be driven by &nbsp;Filecoin for even greater scalablility and flexibility.</p>\n<h3>What’s Next?</h3>\n<p>So &nbsp;there you have it. Five solutions to five requirements for seamless, &nbsp;secure, decentralized photo sharing and backup. Easy 😉. And at a &nbsp;conceptual level, the Textile Thread protocol <em>is</em> &nbsp;relatively simple: blocks of operations chained together to produce a &nbsp;beautiful Thread of photos. But there’s a lot of complexity going on &nbsp;under-the-hood that has required a lot of experimentation, testing, and &nbsp;limit pushing, especially on mobile. And our journey isn’t over yet.</p>\n<p><a href=\"https://twitter.com/Textile01/lists/textile-team/members\">The Textile team</a> &nbsp;is still hard at work iterating, updating, and improving upon what we &nbsp;already have working. For example, we’ll soon to moving to a new offline &nbsp;messaging system that allow us to drop the custom DHT fork, and move &nbsp;back to the public IPFS network. On top of this, our <a href=\"https://github.com/textileio/textile-go/projects/2\">move to more powerful backup</a> &nbsp;and recovery capabilities has us taking new approaches to security, &nbsp;profile management, offline interactions, and much much more. On top of &nbsp;these changes, the team is actively working to modularize the Threads &nbsp;concept and code into its own stand-alone package, which should provide &nbsp;developers with something akin to a Realm and/or Firebase layer for &nbsp;decentralized mobile applications!</p>\n<p>If you are interested in learning more about this stuff, reach out over <a href=\"https://twitter.com/Textile01\">Twitter</a> or <a href=\"https://slack.textile.io/\">Slack</a>, &nbsp;or pull us aside the next time you see us at a conference or event. We’re happy to provide background, thoughts, and opinions on how we &nbsp;think the future of decentralized apps will play out. In the mean time, &nbsp;don’t forget to check out our <a href=\"https://github.com/textileio\">GitHub repos</a> &nbsp;for code and PRs that showcase our current and old implementations. We &nbsp;try to make sure all our development happens out in the open, so you can &nbsp;see things as they develop. Additionally, if you haven’t already, <a href=\"https://www.textile.photos/#cta\">don’t miss out on signing up for our waitlist</a>, where you can get early access to Textile Photos, the beautiful interface to Textile’s Threads.</p>\n</html>",
      "title": "Textile Threads whitepaper… just kidding… a deeper look at the tech behind Textile’s Threads protocol",
      "author": "sanderpick",
      "permlink": "textile-threads-whitepaper-just-kidding-a-deeper-look-at-the-tech-behind-textile-s-threads-protocol",
      "json_metadata": "{\"tags\":[\"mobile\",\"p2p\",\"crypto\",\"blockchain\",\"decentralization\"],\"image\":[\"https://cdn-images-1.medium.com/max/800/1*beyK889WqmNEatN55oBAnA.png\",\"https://cdn-images-1.medium.com/max/800/1*xEE3Pp-LdU_r8qsPvMVS8w.gif\",\"https://cdn-images-1.medium.com/max/800/1*0iCOZW6HrpdQ0izG5O9ciw.png\",\"https://cdn-images-1.medium.com/max/800/1*JsXZAveyMui2izg_-FMh0g.png\",\"https://cdn-images-1.medium.com/max/800/1*h9eNwOauuT7mMeS4o-yGbw.png\",\"https://cdn-images-1.medium.com/max/800/1*3EuEtFHqUtALTczIeZ8_eg.png\"],\"links\":[\"https://medium.com/@carsonfarmer\",\"https://medium.com/@sanderpick\",\"https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14\",\"https://realm.io/\",\"https://firebase.google.com/\",\"https://textile.photos/\",\"https://en.wikipedia.org/wiki/Bitcoin\",\"https://bitcoin.org/en/developer-guide#initial-block-download\",\"https://filecoin.io/\",\"https://en.wikipedia.org/wiki/Peer-to-peer\",\"https://en.wikipedia.org/wiki/Lists_of_network_protocols\",\"https://libp2p.io/\",\"https://ipfs.io/\",\"https://github.com/libp2p/go-libp2p-secio\",\"https://github.com/libp2p/go-stream-security\",\"https://developers.google.com/protocol-buffers/\",\"https://github.com/textileio/textile-go/blob/master/pb/protos/thread.proto\",\"https://github.com/auditdrivencrypto/secure-channel/blob/master/prior-art.md#ipfss-secure-channel\",\"https://github.com/textileio/textile-go/commit/05a269cd5dd72da224c4d2c472abad00a078d4ca\",\"https://medium.com/textileio/the-5-steps-to-end-to-end-encrypted-photo-storage-and-sharing-45ad4aad6b14#ea20\",\"https://en.wikipedia.org/wiki/Kademlia\",\"https://en.wikipedia.org/wiki/Distributed_hash_table\",\"https://github.com/libp2p/go-libp2p-kad-dht\",\"https://en.wikipedia.org/wiki/Hash_table\",\"https://medium.com/textileio/swapping-bits-and-distributing-hashes-on-the-decentralized-web-5da98a3507\",\"https://www.researchgate.net/figure/2-Overlay-and-underlay-view-of-Distributed-Hash-Tables-11_fig2_304495476\",\"https://github.com/libp2p/notes/issues/2#issuecomment-433729343\",\"https://github.com/textileio/textile-go/projects/2\",\"https://github.com/ipfs/research-CRDT/\",\"https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type\",\"https://github.com/orbitdb/ipfs-log\",\"https://www.atlassian.com/git/tutorials/using-branches/git-merge\",\"https://blog.thoughtram.io/git/2014/11/18/the-anatomy-of-a-git-commit.html#meet-the-tree-object\",\"https://twitter.com/Textile01/status/1004436869734543360\",\"https://en.wikipedia.org/wiki/Blockchain\",\"https://medium.com/textileio\",\"https://medium.com/textileio/enabling-the-distributed-web-abf7ab33b638\",\"https://en.wikipedia.org/wiki/Fingerprint_%28computing%29\",\"https://github.com/ipld/cid\",\"https://github.com/multiformats/multihash\",\"https://en.wikipedia.org/wiki/Cryptographic_hash_function\",\"https://en.wikipedia.org/wiki/SHA-3\",\"https://ipfs.io/ipfs/QmcNzGMQ1hUzSnqM2aH1Um8KWLRC4Rd9TyY4kFuYZXWXaF/Textile_Icon_500x500.png\",\"https://github.com/textileio/textile-go\",\"https://twitter.com/Textile01/lists/textile-team/members\",\"https://twitter.com/Textile01\",\"https://slack.textile.io/\",\"https://github.com/textileio\",\"https://www.textile.photos/#cta\"],\"app\":\"steemit/0.1\",\"format\":\"html\"}",
      "parent_author": "",
      "parent_permlink": "mobile"
    }
  ],
  "block": 27323219,
  "trx_id": "1b0fac506cdfb108f722b2e1c911db0abc02ff43",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T16:30:30",
  "virtual_op": false,
  "trx_in_block": 28
}
blocktradesblockchain operation: transfer to vesting completed
2018/11/01 16:28:30
to accountsanderpick
hive vested11.739 HIVE
from accountblocktrades
vesting shares received23674.341006 VESTS
Transaction InfoBlock #27323179/Trx bbfaddccabdd48377726866da7c7493be2eb6bbc
View Raw JSON Data
{
  "op": [
    "transfer_to_vesting_completed",
    {
      "to_account": "sanderpick",
      "hive_vested": "11.739 HIVE",
      "from_account": "blocktrades",
      "vesting_shares_received": "23674.341006 VESTS"
    }
  ],
  "block": 27323179,
  "trx_id": "bbfaddccabdd48377726866da7c7493be2eb6bbc",
  "op_in_trx": 1,
  "timestamp": "2018-11-01T16:28:30",
  "virtual_op": true,
  "trx_in_block": 6
}
blocktradespowered up 11.739 HIVE to @sanderpick
2018/11/01 16:28:30
tosanderpick
fromblocktrades
amount11.739 HIVE
Transaction InfoBlock #27323179/Trx bbfaddccabdd48377726866da7c7493be2eb6bbc
View Raw JSON Data
{
  "op": [
    "transfer_to_vesting",
    {
      "to": "sanderpick",
      "from": "blocktrades",
      "amount": "11.739 HIVE"
    }
  ],
  "block": 27323179,
  "trx_id": "bbfaddccabdd48377726866da7c7493be2eb6bbc",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T16:28:30",
  "virtual_op": false,
  "trx_in_block": 6
}
sanderpickupdated their account properties
2018/11/01 15:23:57
accountsanderpick
memo keySTM5CZugansgXw2GtjWmHu9kiiPrpvpiGHNGGw9d4yeb97zVFsdw3
json metadata{"profile":{"profile_image":"https://cdn.steemitimages.com/DQmcvgiUj7JsQ9yqVgDuehbAwiB7b5sokTH9YUvaMVzbBTz/IMG_8874.jpg","cover_image":"https://ipfs.textile.io/ipfs/QmbmwFiGQG4ZbzcervBRYiVMGqi56oWcSTcx2Do8YaAGZ5","name":"Sander Pick","about":"Co-founder/CTO @ https://textile.io . Having fun with p2p networks, cryptography, and rock climbing. Lover of free speech and the open marketplace of ideas.","location":"Sunnyvale, CA","website":"https://www.textile.io"}}
Transaction InfoBlock #27321889/Trx 598440f34f030f630a06d9526dc248337a0d5a30
View Raw JSON Data
{
  "op": [
    "account_update",
    {
      "account": "sanderpick",
      "memo_key": "STM5CZugansgXw2GtjWmHu9kiiPrpvpiGHNGGw9d4yeb97zVFsdw3",
      "json_metadata": "{\"profile\":{\"profile_image\":\"https://cdn.steemitimages.com/DQmcvgiUj7JsQ9yqVgDuehbAwiB7b5sokTH9YUvaMVzbBTz/IMG_8874.jpg\",\"cover_image\":\"https://ipfs.textile.io/ipfs/QmbmwFiGQG4ZbzcervBRYiVMGqi56oWcSTcx2Do8YaAGZ5\",\"name\":\"Sander Pick\",\"about\":\"Co-founder/CTO @ https://textile.io . Having fun with p2p networks, cryptography, and rock climbing. Lover of free speech and the open marketplace of ideas.\",\"location\":\"Sunnyvale, CA\",\"website\":\"https://www.textile.io\"}}"
    }
  ],
  "block": 27321889,
  "trx_id": "598440f34f030f630a06d9526dc248337a0d5a30",
  "op_in_trx": 0,
  "timestamp": "2018-11-01T15:23:57",
  "virtual_op": false,
  "trx_in_block": 29
}
steemdelegated 6.136 HP to @sanderpick
2018/06/01 19:12:09
delegateesanderpick
delegatorsteem
vesting shares9961.473965 VESTS
Transaction InfoBlock #22948884/Trx b0c163e4190864f4e94ce580e2cac26b3b4a9b52
View Raw JSON Data
{
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "sanderpick",
      "delegator": "steem",
      "vesting_shares": "9961.473965 VESTS"
    }
  ],
  "block": 22948884,
  "trx_id": "b0c163e4190864f4e94ce580e2cac26b3b4a9b52",
  "op_in_trx": 0,
  "timestamp": "2018-06-01T19:12:09",
  "virtual_op": false,
  "trx_in_block": 49
}
steemdelegated 18.750 HP to @sanderpick
2018/03/02 18:42:51
delegateesanderpick
delegatorsteem
vesting shares30438.477360 VESTS
Transaction InfoBlock #20330247/Trx 09355c3a99cb6535147741c6b9d40edfb78482b6
View Raw JSON Data
{
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "sanderpick",
      "delegator": "steem",
      "vesting_shares": "30438.477360 VESTS"
    }
  ],
  "block": 20330247,
  "trx_id": "09355c3a99cb6535147741c6b9d40edfb78482b6",
  "op_in_trx": 4,
  "timestamp": "2018-03-02T18:42:51",
  "virtual_op": false,
  "trx_in_block": 34
}
sanderpickupdated their account properties
2018/03/02 17:55:57
accountsanderpick
memo keySTM5CZugansgXw2GtjWmHu9kiiPrpvpiGHNGGw9d4yeb97zVFsdw3
json metadata{"profile":{"profile_image":"https://pbs.twimg.com/profile_images/840041206000246785/He5ot_rH_400x400.jpg","cover_image":"https://ipfs.textile.io/ipfs/QmbmwFiGQG4ZbzcervBRYiVMGqi56oWcSTcx2Do8YaAGZ5","name":"Sander Pick","about":"I like hard problems, focus, open spaces, cryptography, decentralization, rock climbing. Montanan.","location":"Sunnyvale, CA","website":"https://www.textile.io"}}
Transaction InfoBlock #20329309/Trx 6cc448eb84ac0fabc51308c7bf7df7aa1e788853
View Raw JSON Data
{
  "op": [
    "account_update",
    {
      "account": "sanderpick",
      "memo_key": "STM5CZugansgXw2GtjWmHu9kiiPrpvpiGHNGGw9d4yeb97zVFsdw3",
      "json_metadata": "{\"profile\":{\"profile_image\":\"https://pbs.twimg.com/profile_images/840041206000246785/He5ot_rH_400x400.jpg\",\"cover_image\":\"https://ipfs.textile.io/ipfs/QmbmwFiGQG4ZbzcervBRYiVMGqi56oWcSTcx2Do8YaAGZ5\",\"name\":\"Sander Pick\",\"about\":\"I like hard problems, focus, open spaces, cryptography, decentralization, rock climbing. Montanan.\",\"location\":\"Sunnyvale, CA\",\"website\":\"https://www.textile.io\"}}"
    }
  ],
  "block": 20329309,
  "trx_id": "6cc448eb84ac0fabc51308c7bf7df7aa1e788853",
  "op_in_trx": 0,
  "timestamp": "2018-03-02T17:55:57",
  "virtual_op": false,
  "trx_in_block": 31
}
sanderpickupdated their account properties
2018/03/02 17:50:21
accountsanderpick
memo keySTM5CZugansgXw2GtjWmHu9kiiPrpvpiGHNGGw9d4yeb97zVFsdw3
json metadata{"profile":{"profile_image":"https://ipfs.textile.io/ipfs/QmUZ4RbHCahBsfjJWGJhee8u7o8wuhTzhabJMnhik7TwQ3","cover_image":"https://ipfs.textile.io/ipfs/QmbmwFiGQG4ZbzcervBRYiVMGqi56oWcSTcx2Do8YaAGZ5","name":"Sander Pick","about":"I like hard problems, focus, open spaces, cryptography, decentralization, rock climbing. Montanan.","location":"Sunnyvale, CA","website":"https://www.textile.io"}}
Transaction InfoBlock #20329197/Trx 1fd600b64ea5ee1e65f46b4a84334ee44f96f40e
View Raw JSON Data
{
  "op": [
    "account_update",
    {
      "account": "sanderpick",
      "memo_key": "STM5CZugansgXw2GtjWmHu9kiiPrpvpiGHNGGw9d4yeb97zVFsdw3",
      "json_metadata": "{\"profile\":{\"profile_image\":\"https://ipfs.textile.io/ipfs/QmUZ4RbHCahBsfjJWGJhee8u7o8wuhTzhabJMnhik7TwQ3\",\"cover_image\":\"https://ipfs.textile.io/ipfs/QmbmwFiGQG4ZbzcervBRYiVMGqi56oWcSTcx2Do8YaAGZ5\",\"name\":\"Sander Pick\",\"about\":\"I like hard problems, focus, open spaces, cryptography, decentralization, rock climbing. Montanan.\",\"location\":\"Sunnyvale, CA\",\"website\":\"https://www.textile.io\"}}"
    }
  ],
  "block": 20329197,
  "trx_id": "1fd600b64ea5ee1e65f46b4a84334ee44f96f40e",
  "op_in_trx": 0,
  "timestamp": "2018-03-02T17:50:21",
  "virtual_op": false,
  "trx_in_block": 45
}
steemaccount was created by @steem
2018/03/02 17:19:30
creatorsteem
new account namesanderpick
initial delegation30690.000000 VESTS
initial vesting shares204.285596 VESTS
Transaction InfoBlock #20328580/Trx 4c8f11dfdc8cfa9a2e2861c32dd4dadf616a2073
View Raw JSON Data
{
  "op": [
    "account_created",
    {
      "creator": "steem",
      "new_account_name": "sanderpick",
      "initial_delegation": "30690.000000 VESTS",
      "initial_vesting_shares": "204.285596 VESTS"
    }
  ],
  "block": 20328580,
  "trx_id": "4c8f11dfdc8cfa9a2e2861c32dd4dadf616a2073",
  "op_in_trx": 1,
  "timestamp": "2018-03-02T17:19:30",
  "virtual_op": true,
  "trx_in_block": 6
}
steemcreated a new account: @sanderpick
2018/03/02 17:19:30
fee0.100 HIVE
owner{"key_auths":[["STM5miWoPgXh9Coxgg462uDpuAuBuVa5jmGEEqCxFLCzxuWovxvhy",1]],"account_auths":[],"weight_threshold":1}
active{"key_auths":[["STM87JJzyvJ9e8Rf6vm72fDaQtET6dKWPqHycSmfbvKziP4QpGHQw",1]],"account_auths":[],"weight_threshold":1}
creatorsteem
posting{"key_auths":[["STM6BTaccrcMfAkSe4csndTguQMXXD6EXtRXE6wSeFW5p67EcZi3k",1]],"account_auths":[],"weight_threshold":1}
memo keySTM5CZugansgXw2GtjWmHu9kiiPrpvpiGHNGGw9d4yeb97zVFsdw3
delegation30690.000000 VESTS
extensions[]
json metadata{}
new account namesanderpick
Transaction InfoBlock #20328580/Trx 4c8f11dfdc8cfa9a2e2861c32dd4dadf616a2073
View Raw JSON Data
{
  "op": [
    "account_create_with_delegation",
    {
      "fee": "0.100 HIVE",
      "owner": {
        "key_auths": [
          [
            "STM5miWoPgXh9Coxgg462uDpuAuBuVa5jmGEEqCxFLCzxuWovxvhy",
            1
          ]
        ],
        "account_auths": [],
        "weight_threshold": 1
      },
      "active": {
        "key_auths": [
          [
            "STM87JJzyvJ9e8Rf6vm72fDaQtET6dKWPqHycSmfbvKziP4QpGHQw",
            1
          ]
        ],
        "account_auths": [],
        "weight_threshold": 1
      },
      "creator": "steem",
      "posting": {
        "key_auths": [
          [
            "STM6BTaccrcMfAkSe4csndTguQMXXD6EXtRXE6wSeFW5p67EcZi3k",
            1
          ]
        ],
        "account_auths": [],
        "weight_threshold": 1
      },
      "memo_key": "STM5CZugansgXw2GtjWmHu9kiiPrpvpiGHNGGw9d4yeb97zVFsdw3",
      "delegation": "30690.000000 VESTS",
      "extensions": [],
      "json_metadata": "{}",
      "new_account_name": "sanderpick"
    }
  ],
  "block": 20328580,
  "trx_id": "4c8f11dfdc8cfa9a2e2861c32dd4dadf616a2073",
  "op_in_trx": 0,
  "timestamp": "2018-03-02T17:19:30",
  "virtual_op": false,
  "trx_in_block": 6
}

Account Metadata

POSTING JSON METADATA
profile{"profile_image":"https://cdn.steemitimages.com/DQmcvgiUj7JsQ9yqVgDuehbAwiB7b5sokTH9YUvaMVzbBTz/IMG_8874.jpg","cover_image":"https://ipfs.textile.io/ipfs/QmbmwFiGQG4ZbzcervBRYiVMGqi56oWcSTcx2Do8YaAGZ5","name":"Sander Pick","about":"Co-founder/CTO @ https://textile.io . Having fun with p2p networks, cryptography, and rock climbing. Lover of free speech and the open marketplace of ideas.","location":"Sunnyvale, CA","website":"https://www.textile.io"}
JSON METADATA
profile{"profile_image":"https://cdn.steemitimages.com/DQmcvgiUj7JsQ9yqVgDuehbAwiB7b5sokTH9YUvaMVzbBTz/IMG_8874.jpg","cover_image":"https://ipfs.textile.io/ipfs/QmbmwFiGQG4ZbzcervBRYiVMGqi56oWcSTcx2Do8YaAGZ5","name":"Sander Pick","about":"Co-founder/CTO @ https://textile.io . Having fun with p2p networks, cryptography, and rock climbing. Lover of free speech and the open marketplace of ideas.","location":"Sunnyvale, CA","website":"https://www.textile.io"}
{
  "posting_json_metadata": {
    "profile": {
      "profile_image": "https://cdn.steemitimages.com/DQmcvgiUj7JsQ9yqVgDuehbAwiB7b5sokTH9YUvaMVzbBTz/IMG_8874.jpg",
      "cover_image": "https://ipfs.textile.io/ipfs/QmbmwFiGQG4ZbzcervBRYiVMGqi56oWcSTcx2Do8YaAGZ5",
      "name": "Sander Pick",
      "about": "Co-founder/CTO @ https://textile.io . Having fun with p2p networks, cryptography, and rock climbing. Lover of free speech and the open marketplace of ideas.",
      "location": "Sunnyvale, CA",
      "website": "https://www.textile.io"
    }
  },
  "json_metadata": {
    "profile": {
      "profile_image": "https://cdn.steemitimages.com/DQmcvgiUj7JsQ9yqVgDuehbAwiB7b5sokTH9YUvaMVzbBTz/IMG_8874.jpg",
      "cover_image": "https://ipfs.textile.io/ipfs/QmbmwFiGQG4ZbzcervBRYiVMGqi56oWcSTcx2Do8YaAGZ5",
      "name": "Sander Pick",
      "about": "Co-founder/CTO @ https://textile.io . Having fun with p2p networks, cryptography, and rock climbing. Lover of free speech and the open marketplace of ideas.",
      "location": "Sunnyvale, CA",
      "website": "https://www.textile.io"
    }
  }
}

Auth Keys

Owner
Single Signature
Public Keys
STM5miWoPgXh9Coxgg462uDpuAuBuVa5jmGEEqCxFLCzxuWovxvhy1/1
Active
Single Signature
Public Keys
STM87JJzyvJ9e8Rf6vm72fDaQtET6dKWPqHycSmfbvKziP4QpGHQw1/1
Posting
Single Signature
Public Keys
STM6BTaccrcMfAkSe4csndTguQMXXD6EXtRXE6wSeFW5p67EcZi3k1/1
Memo
STM5CZugansgXw2GtjWmHu9kiiPrpvpiGHNGGw9d4yeb97zVFsdw3
{
  "owner": {
    "weight_threshold": 1,
    "account_auths": [],
    "key_auths": [
      [
        "STM5miWoPgXh9Coxgg462uDpuAuBuVa5jmGEEqCxFLCzxuWovxvhy",
        1
      ]
    ]
  },
  "active": {
    "weight_threshold": 1,
    "account_auths": [],
    "key_auths": [
      [
        "STM87JJzyvJ9e8Rf6vm72fDaQtET6dKWPqHycSmfbvKziP4QpGHQw",
        1
      ]
    ]
  },
  "posting": {
    "weight_threshold": 1,
    "account_auths": [],
    "key_auths": [
      [
        "STM6BTaccrcMfAkSe4csndTguQMXXD6EXtRXE6wSeFW5p67EcZi3k",
        1
      ]
    ]
  },
  "memo": "STM5CZugansgXw2GtjWmHu9kiiPrpvpiGHNGGw9d4yeb97zVFsdw3"
}

Witness Votes

0 / 30
No active witness votes.
[]