{"id":9649,"date":"2020-02-27T13:00:00","date_gmt":"2020-02-27T12:00:00","guid":{"rendered":"https:\/\/blog.mi.hdm-stuttgart.de\/?p=9649"},"modified":"2023-08-06T21:44:19","modified_gmt":"2023-08-06T19:44:19","slug":"ipfs-the-interplanetary-file-system-demystified","status":"publish","type":"post","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2020\/02\/27\/ipfs-the-interplanetary-file-system-demystified\/","title":{"rendered":"IPFS: the InterPlanetary File System demystified"},"content":{"rendered":"<p><img decoding=\"async\" src=\"\/wp-content\/uploads\/2020\/02\/ipfs-logo-vector-ice-text.png\" style=\"height: 120px;margin-right: 8px\" align=\"left\"> In this article we will explore IPFS, the InterPlanetary File System. IPFS is a system for storing and accessing files, websites and other kinds of data \u2014 just as the Web we enjoy using every day \u2014 but unlike the Web, IPFS is peer-to-peer based and automatically distributes its content across the network.<\/p>\n<p><!--more--><\/p>\n<p>A copy of this post is available on the IPFS network at <code class=\"\" data-line=\"\">\/ipns\/ipfs.leonklingele.de\/edu\/hdm\/ipfs\/index.html<\/code>:<br \/>\n<a href=\"https:\/\/ipfs.io\/ipns\/ipfs.leonklingele.de\/edu\/hdm\/ipfs\/\">https:\/\/ipfs.io\/ipns\/ipfs.leonklingele.de\/edu\/hdm\/ipfs\/<\/a> (<em>seriously, go read it over there, the syntax highlighting in this blog is terrible!<\/em>)<\/p>\n<p>First, some terminology:<\/p>\n<ul>\n<li><strong>IPFS<\/strong>, in uppercase letters, as used throughout this post, refers to the<br \/>\n<em>IPFS protocol<\/em><\/li>\n<li><strong><code class=\"\" data-line=\"\">ipfs<\/code><\/strong>, in lowercase letters, refers to the <a href=\"https:\/\/github.com\/ipfs\/go-ipfs\"><em>IPFS command line utility<\/em><\/a> written in Go<\/li>\n<\/ul>\n<h2>Introducing distributed networks<\/h2>\n<p>From its outset, the Internet<sup id=\"fnref-9649-internet\"><a href=\"#fn-9649-internet\" class=\"jetpack-footnote\">1<\/a><\/sup> was designed to be a decentralized network, able to deliver data packets and find alternate routing paths in case of a network or node failure.<br \/>\nWhile the Internet itself is resilient to failures<sup id=\"fnref-9649-failures\"><a href=\"#fn-9649-failures\" class=\"jetpack-footnote\">2<\/a><\/sup>, the vast majority of content hosted on it is served by centralized organizations and servers, rendering it inaccessible in case of server or network failures.<\/p>\n<p>In a <em>centralized computer<\/em> network, a single, <em>central node<\/em> controls the communication flow between all other nodes. If that central hub goes down, no communication on the network can be made, the network becomes completely unusable.<\/p>\n<p>A <em>decentralized network<\/em> on the other hand is a network where multiple such hubs exist. There still are <em>central nodes<\/em> which need to be passed by data packets in most cases, but when these central hubs crash, at least part of the network will continue to function.<\/p>\n<p>A <em>distributed network<\/em> is the most resilient kind of network where any single node can fail while all remaining nodes are still able to communicate with each other.<\/p>\n<p><img decoding=\"async\" src=\"\/wp-content\/uploads\/2020\/02\/paul-baran-distributed-communications-network.png\" alt=\"Paul Baran, 1962. On distributed communications network\"><\/p>\n<div style=\"text-align: right\"><em>Image source: Paul Baran, 1962. On distributed communications network, pp. 3\u20134<\/em><\/div>\n<h2>IPFS basics<\/h2>\n<p>IPFS creates such a distributed network on top of the Internet, although other types of networks are supported as well. IPFS does not rely on or assume access to IP.<\/p>\n<p>Instead of addressing data by its <em>address<\/em> such as a domain name or IP address, IPFS addresses data by its <em>content<\/em><sup id=\"fnref-9649-content_hash\"><a href=\"#fn-9649-content_hash\" class=\"jetpack-footnote\">3<\/a><\/sup>.<\/p>\n<p>Some of the benefits include:<\/p>\n<ul>\n<li>Making files available from many different locations (similar to BitTorrent)<\/li>\n<li>Making content accessible elsewhere when a server hosting the content goes offline, e.g. by an attack (<em>resilience<\/em>)<\/li>\n<li>Caching becomes a no-brainer since content is addressed by its <em>content<\/em><sup id=\"fnref2:9649-content_hash\"><a href=\"#fn-9649-content_hash\" class=\"jetpack-footnote\">3<\/a><\/sup><\/li>\n<li>Making it harder to censor content<\/li>\n<li>Speeding up content delivery<sup id=\"fnref-9649-origin\"><a href=\"#fn-9649-origin\" class=\"jetpack-footnote\">4<\/a><\/sup>, similar to <em>CDNs<\/em> and other P2P networks<\/li>\n<li>Implicit trust is given by accessing content by its hash<\/li>\n<\/ul>\n<h2>Diving into the <code class=\"\" data-line=\"\">ipfs<\/code> command line utility<\/h2>\n<p>Let&#8217;s get our hands dirty and start exploring the <a href=\"https:\/\/github.com\/ipfs\/go-ipfs\"><code class=\"\" data-line=\"\">ipfs<\/code> CLI utility<\/a> along with some details of the inner workings of the IPFS protocol.<\/p>\n<p><em>Unfortunately, at the time of this writing, no official Debian packages for <code class=\"\" data-line=\"\">ipfs<\/code> exist which means we need to install the tool on our own.<\/em><\/p>\n<p>We first show how to get <code class=\"\" data-line=\"\">ipfs<\/code> up and running with Docker &amp; Docker Compose, and provide an alternative way by installing it directly to Debian-based distros as explained <a href=\"#installing-on-debian-without-docker\">further below<\/a>.<\/p>\n<h3>Installing <code class=\"\" data-line=\"\">ipfs<\/code> with Docker &amp; Docker Compose<\/h3>\n<p>The easiest way to install <code class=\"\" data-line=\"\">ipfs<\/code> at the time of writing is to run it within Docker. The procedure is straightforward and only requires the <code class=\"\" data-line=\"\">docker<\/code> and <code class=\"\" data-line=\"\">docker-compose<\/code> tools to be installed.<\/p>\n<p>At first, change to a dedicated directory where all our files for <code class=\"\" data-line=\"\">ipfs<\/code> will be put. For the purposes of demonstration, we use a temporary directory. Be advised that any data stored there will be lost on a system reboot.<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ cd $(mktemp -d)\n<\/code><\/pre>\n<p>We start by creating a <code class=\"\" data-line=\"\">docker-compose.yml<\/code> file as follows:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ cat &lt;&lt;EOF&gt; docker-compose.yml\nversion: &quot;3&quot;\n\nservices:\n  ipfs:\n    image: ipfs\/go-ipfs:latest\n    restart: unless-stopped\n    cap_drop:\n      - ALL\n    cap_add:\n      - SETUID\n      - SETGID\n      - CHOWN\n    ports:\n      - &quot;4001:4001&quot; # Swarm TCP\n      - &quot;127.0.0.1:5001:5001&quot; # Daemon API\n      - &quot;127.0.0.1:8080:8080&quot; # Web Gateway\n      - &quot;127.0.0.1:8081:8081&quot; # Swarm Websockets\n    working_dir: \/shared\n    volumes:\n      - &quot;.\/data\/ipfs:\/data\/ipfs&quot;\n      - &quot;.\/data\/shared:\/shared&quot;\nEOF\n<\/code><\/pre>\n<p>and launch the Docker container, then exec into it with:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ docker-compose up -d\n\n$ docker-compose exec ipfs sh\n<\/code><\/pre>\n<p>That&#8217;s it. We are now ready to use the <code class=\"\" data-line=\"\">ipfs<\/code> CLI utility.<\/p>\n<h3>Installing <code class=\"\" data-line=\"\">ipfs<\/code> on Debian without Docker<\/h3>\n<p>First, install two dependencies which do not ship by default:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\"># As root\n$ apt install tar curl\n<\/code><\/pre>\n<p>and continue by changing to a dedicated directory where all our files for <code class=\"\" data-line=\"\">ipfs<\/code> will be put.<br \/>\nAgain, be advised that any data put to a temporary directory as used in this tutorial will be lost on a system reboot.<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ cd $(mktemp -d)\n<\/code><\/pre>\n<p>Let&#8217;s install the CLI tool!<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ VERSION=0.4.23\n$ curl -O https:\/\/dist.ipfs.io\/go-ipfs\/v${VERSION}\/go-ipfs_v${VERSION}_linux-amd64.tar.gz\n<\/code><\/pre>\n<p>Verify the tarball&#8217;s integrity and unpack it:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ sha256sum go-ipfs_v${VERSION}_linux-amd64.tar.gz\n639492d0aec98f845d7de8cdb251389bcac924d9f3940921504481923b532e2f  go-ipfs_v0.4.23_linux-amd64.tar.gz\n\n$ tar xzfv go-ipfs_v${VERSION}_linux-amd64.tar.gz\ngo-ipfs\/install.sh\ngo-ipfs\/ipfs\ngo-ipfs\/LICENSE\ngo-ipfs\/README.md\n<\/code><\/pre>\n<p>Continue as follows:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\"># Copy the ipfs binary to a dir in your path\n$ cp go-ipfs\/ipfs \/usr\/local\/bin\/\n\n# Set up an ipfs user so the binary does not run as root\n$ adduser --gecos ipfs --disabled-password ipfs\n\n# Set up a systemd service to make starting and stopping the IPFS daemon easy\n$ cat &lt;&lt;eof&gt; \/etc\/systemd\/system\/ipfs.service\n[Unit]\nDescription=IPFS Daemon\nAfter=network.target&lt;\/eof&gt;\n\n[Service]\nExecStart=\/usr\/local\/bin\/ipfs daemon --init --migrate --enable-gc\nKillSignal=SIGINT\nUser=ipfs\nGroup=ipfs\n\n[Install]\nWantedBy=multi-user.target\nEOF\n\n# Reload the systemd daemon\n$ systemctl daemon-reload\n\n# Enable the ipfs service so it automatically starts upon boot\n$ systemctl enable ipfs\n<\/code><\/pre>\n<p>The IPFS daemon can now be started with a simple<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ systemctl start ipfs\n<\/code><\/pre>\n<p>Check that it&#8217;s really up and running<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ systemctl status ipfs\n[..]\nFeb 25 18:00:00 service-ipfs ipfs[5055]: Daemon is ready\n<\/code><\/pre>\n<p>Nice! Now configure your firewall to open up port <code class=\"\" data-line=\"\">4001\/tcp<\/code> which is required<br \/>\nto connect to other peers:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ ufw allow 4001\/tcp\n<\/code><\/pre>\n<p>As a last step, switch to the <code class=\"\" data-line=\"\">ipfs<\/code> user.<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ su ipfs\n<\/code><\/pre>\n<p>We are now ready to use the <code class=\"\" data-line=\"\">ipfs<\/code> CLI utility.<\/p>\n<h3>Exploring the <code class=\"\" data-line=\"\">ipfs<\/code> CLI utility<\/h3>\n<p>Finally it&#8217;s time to play around with <code class=\"\" data-line=\"\">ipfs<\/code>!<\/p>\n<p><code class=\"\" data-line=\"\">ipfs<\/code> has already been initialized (<code class=\"\" data-line=\"\">ipfs init<\/code>) and its daemon was started (<code class=\"\" data-line=\"\">ipfs daemon<\/code>). Upon first startup, the IPFS node created a public\/private key pair which uniquely identifies the node on the IPFS network. The public key is hashed<sup id=\"fnref-9649-hash\"><a href=\"#fn-9649-hash\" class=\"jetpack-footnote\">5<\/a><\/sup>, <a href=\"https:\/\/github.com\/libp2p\/go-libp2p-core\/blob\/v0.3.1\/peer\/peer.go#L221-L233\">yielding<\/a> the peer&#8217;s unique ID which will become useful <a href=\"#enter-ipns-the-interplanetary-naming-system\">later on<\/a>.<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\"># Show own peer ID\n$ ipfs id --format=&#039;&lt;id&gt;\\n&#039;\nQmXWf53PNW5nSrP2voZg9GHfYpqPWrYo7677saX6yFV8Z1\n<\/code><\/pre>\n<p>The node should already have connected to other peers on the IPFS network found through the <a href=\"https:\/\/docs.ipfs.io\/guides\/examples\/bootstrap\/\">bootstrapping \/ seeding process<\/a>, similar to how nearly every other P2P network out there does (e.g. <a href=\"https:\/\/github.com\/bitcoin\/bitcoin\/blob\/v0.19.0.1\/src\/chainparams.cpp\/#L111-L123\">Bitcoin<\/a> and <a href=\"https:\/\/github.com\/monero-project\/monero\/blob\/v0.15.0.1\/src\/p2p\/net_node.inl#L596-L631\">Monero<\/a>):<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ ipfs swarm peers\n[..] long list of peers, an excerpt:\n\/ip4\/37.120.190.6\/tcp\/4001\/ipfs\/Qma5q8kiKopYw1G3sSTwtRXDgx1AQ7a7jgJy6gdERvvWEY\n\/ip4\/94.16.118.23\/tcp\/4001\/ipfs\/Qmdoy815C6fWDiUCeCq8ETVBwaWN2gsdmGTf9f4ST9P7X7\n\/ip4\/94.16.118.250\/tcp\/4001\/ipfs\/QmV1gHWEBkfVDXRNbrUJ6qLfk9GiCR5gSbUGYdVXwzZCeK\n<\/code><\/pre>\n<p>Time to ping them:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ ipfs ping Qma5q8kiKopYw1G3sSTwtRXDgx1AQ7a7jgJy6gdERvvWEY\nPING Qma5q8kiKopYw1G3sSTwtRXDgx1AQ7a7jgJy6gdERvvWEY.\nPong received: time=0.56 ms\nPong received: time=0.57 ms\nPong received: time=0.61 ms\n^C\nAverage latency: 0.58ms\n\n# \u2026 smooth!\n<\/code><\/pre>\n<p>Upon first connecting, peers exchange their public keys and check whether the hash of their partner&#8217;s public key really equals the remote&#8217;s node ID. If not, the connection is terminated.<\/p>\n<p>Every node has a local storage where IPFS blocks such as file objects are stored. This storage is either in RAM, some kind of database or simply on the node&#8217;s filesystem. Ultimately, all blocks available on IPFS are in some node&#8217;s local storage. When an object is requested, a peer serving the file is searched, eventually found, then downloaded from and stored temporarily in the local storage. This provides fast lookups of the same object for some time thereafter. Stored blocks can optionally be pinned so the block will stay in the local nodes&#8217;s storage forever without expiring.<\/p>\n<h4>Adding files to IPFS<\/h4>\n<p>Every node on IPFS \u2014 including our own \u2014 is capable of hosting files and delivering them to other peers. Adding new files to IPFS is easy:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\"># Create a new file on IPFS\n$ echo &quot;Hello World, today is $(date &#039;+%Y-%m-%d&#039;)&quot; | ipfs add\nadded QmVDE7P8cWERGeoexGtLsLGzABoiVQvwaJP7FTv4X6dEbk QmVDE7P8cWERGeoexGtLsLGzABoiVQvwaJP7FTv4X6dEbk\n33 B \/ 33 B [============================================================================] 100.00%\n\n# Note: if you run the command above it will return a different hash as the file will be a different.\n<\/code><\/pre>\n<p><code class=\"\" data-line=\"\">ipfs<\/code> adds the file to the local object storage and returns the hash of it: <code class=\"\" data-line=\"\">QmVDE7P8cWERGeoexGtLsLGzABoiVQvwaJP7FTv4X6dEbk<\/code>. The file&#8217;s hash is added to the DHT and the local node starts serving the file to other nodes requesting it by its hash value.<\/p>\n<p>As the hash uniquely identifies the file, we can retrieve it with:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\"># View the file we just added\n$ ipfs cat QmVDE7P8cWERGeoexGtLsLGzABoiVQvwaJP7FTv4X6dEbk\nHello World, today is 2020-02-27\n\n# Yup, working fine!\n<\/code><\/pre>\n<p>Upon adding the file, <code class=\"\" data-line=\"\">ipfs<\/code> also automatically announces its existence to other peers on the network. (Try to <code class=\"\" data-line=\"\">ipfs cat<\/code> it from another node \u2014 it will return the same content!)<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">In IPFS, data distribution happens by exchanging blocks with peers using a\nBitTorrent-inspired protocol: BitSwap. Like BitTorrent, BitSwap peers are\nlooking to acquire a set of blocks (the want_list), and have another set of\nblocks to offer in exchange (the have_list). Unlike BitTorrent, BitSwap is not\nlimited to the blocks in one torrent. BitSwap operates as a persistent\nmarketplace where nodes can acquire the blocks they need, regardless of what\nfiles those blocks are part of. The blocks could come from completely unrelated\nfiles in the filesystem. Nodes come together to barter in the marketplace.\n\nIn the base case, BitSwap nodes have to provide direct value to each other in\nthe form of blocks. This works fine when the distribution of blocks across nodes\nis complementary, meaning they have what the other wants. Often, this will not\nbe the case. In some cases, nodes must work for their blocks. In the case that\na node has nothing that its peers want (or nothing at all), it seeks the pieces\nits peers want, with lower priority than what the node wants itself. This\nincentivizes nodes to cache and disseminate rare pieces, even if they are not\ninterested in them directly.\n\nThe protocol must also incentivize nodes to seed when they do not need anything\nin particular, as they might have the blocks others want. Thus, BitSwap nodes\nsend blocks to their peers optimistically, expecting the debt to be repaid. But\nleeches (free-loading nodes that never share) must be protected against. A\nsimple credit-like system solves the problem:\n\n1. Peers track their balance (in bytes verified) with other nodes.\n2. Peers send blocks to debtor peers probabilistically, according to a function\nthat falls as debt increases.\n\nNote that if a node decides not to send to a peer, the node subsequently ignores\nthe peer for a while. This prevents senders from trying to game the probability\nby just causing more dice-rolls.\n\nThe debt ratio is a measure of trust: lenient to debts between nodes that have\npreviously exchanged lots of data successfully, and merciless to unknown,\nuntrusted nodes.\n<\/code><\/pre>\n<div style=\"text-align: right\"><em>Source: <a href=\"https:\/\/github.com\/ipfs\/papers\/raw\/master\/ipfs-cap2pfs\/ipfs-p2p-file-system.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">IPFS whitepaper<\/a> which continues to describe this in more detail.<\/em><\/div>\n<p>Adding files was easy. Let&#8217;s deploy a small static website to IPFS.<\/p>\n<h4>Deploying a static website to IPFS<\/h4>\n<p>First, a website is required! Fruits are awesome, so let&#8217;s showcase our favorite fruits on a website:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ WEBDIR=demo\/websites\n$ SITEDIR=$WEBDIR\/yummyfruits\n$ mkdir -p $SITEDIR\n$ echo &#039;&lt;h1 style=&quot;color: green;&quot;&gt;I really like green Apples! \ud83c\udf4f&lt;\/h1&gt;&#039; &gt; $SITEDIR\/apple.html\n$ echo &#039;&lt;h1 style=&quot;color: yellow;&quot;&gt;Bananas are one of my favorite fruits! \ud83c\udf4c&lt;\/h1&gt;&#039; &gt; $SITEDIR\/banana.html\n$ ipfs add -r $WEBDIR\nadded QmUx6PWeRAPxCTqmK4eGq95KnvPCJwNhfU1cgw5RWCEG8y websites\/yummyfruits\/apple.html\nadded QmZkMyLFKAQqRXer1MExU3vnfAFmxCN7rFkpmsUQBG81UN websites\/yummyfruits\/banana.html\nadded QmbVPuewTkDPSLJku69JFkAox3VUcMiokK2fk2HniT7e1u websites\/yummyfruits\nadded QmeoPDguuLaYsZXnniL5Be7t9hmbU55XU6B8LE5cuS7TJr websites\n140 B \/ 140 B [==========================================================================] 100.00%\n<\/code><\/pre>\n<p>\u2026 the whole website has just been added to IPFS and is ready to be requested.<\/p>\n<p>To list the websites we currently host, <code class=\"\" data-line=\"\">ipfs ls<\/code> the hash of the <code class=\"\" data-line=\"\">websites<\/code> object:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\"># Show our websites\n$ ipfs ls QmeoPDguuLaYsZXnniL5Be7t9hmbU55XU6B8LE5cuS7TJr\nQmbVPuewTkDPSLJku69JFkAox3VUcMiokK2fk2HniT7e1u - yummyfruits\/\n<\/code><\/pre>\n<p>Paths work as they do in traditional UNIX filesystems and the Web:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\"># List files on the YummyFruits website\n$ ipfs ls QmeoPDguuLaYsZXnniL5Be7t9hmbU55XU6B8LE5cuS7TJr\/yummyfruits\nQmUx6PWeRAPxCTqmK4eGq95KnvPCJwNhfU1cgw5RWCEG8y 64 apple.html\nQmZkMyLFKAQqRXer1MExU3vnfAFmxCN7rFkpmsUQBG81UN 76 banana.html\n<\/code><\/pre>\n<p>To retrieve webpages from our YummyFruits website:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ ipfs cat QmeoPDguuLaYsZXnniL5Be7t9hmbU55XU6B8LE5cuS7TJr\/yummyfruits\/apple.html\n&lt;h1 style=&quot;color: green;&quot;&gt;I really like green Apples! \ud83c\udf4f&lt;\/h1&gt;\n$ ipfs cat QmeoPDguuLaYsZXnniL5Be7t9hmbU55XU6B8LE5cuS7TJr\/yummyfruits\/banana.html\n&lt;h1 style=&quot;color: yellow;&quot;&gt;Bananas are one of my favorite fruits! \ud83c\udf4c&lt;\/h1&gt;\n<\/code><\/pre>\n<p>Alternatively, the files can be accessed directly using their hash:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\"># Resolve hash of apple.html, or simply look up its hash above\n$ ipfs resolve QmeoPDguuLaYsZXnniL5Be7t9hmbU55XU6B8LE5cuS7TJr\/yummyfruits\/apple.html\n\/ipfs\/QmUx6PWeRAPxCTqmK4eGq95KnvPCJwNhfU1cgw5RWCEG8y\n\n# Address apple.html directly by its hash\n$ ipfs cat QmUx6PWeRAPxCTqmK4eGq95KnvPCJwNhfU1cgw5RWCEG8y\n&lt;h1 style=&quot;color: green;&quot;&gt;I really like green Apples! \ud83c\udf4f&lt;\/h1&gt;\n<\/code><\/pre>\n<p>A different notation to achieve exactly the same is to prefix the IPFS object<br \/>\nwith <code class=\"\" data-line=\"\">\/ipfs\/<\/code>:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\"># Address apple.html directly by its hash and the \/ipfs\/ prefix\n$ ipfs cat \/ipfs\/QmUx6PWeRAPxCTqmK4eGq95KnvPCJwNhfU1cgw5RWCEG8y\n&lt;h1 style=&quot;color: green;&quot;&gt;I really like green Apples! \ud83c\udf4f&lt;\/h1&gt;\n<\/code><\/pre>\n<p>Other prefixes are, for example, <code class=\"\" data-line=\"\">\/ipns\/<\/code> which we&#8217;ll explore <a href=\"#enter-ipns-the-interplanetary-naming-system\">later on<\/a>.<\/p>\n<p>So far so good. Wouldn&#8217;t it be cool to view our website in an <em>actual browser<\/em>?<\/p>\n<h5>Accessing our website in a web browser<\/h5>\n<p>The IPFS daemon sets up a Web Gateway running locally on port <code class=\"\" data-line=\"\">8080<\/code> which can be accessed using a web browser or with other clients such as <code class=\"\" data-line=\"\">curl<\/code>:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ curl localhost:8080\/ipfs\/QmeoPDguuLaYsZXnniL5Be7t9hmbU55XU6B8LE5cuS7TJr\/yummyfruits\/apple.html\n&lt;h1 style=&quot;color: green;&quot;&gt;I really like green Apples! \ud83c\udf4f&lt;\/h1&gt;\n<\/code><\/pre>\n<p><img decoding=\"async\" src=\"\/wp-content\/uploads\/2020\/02\/website-ipfs.png\" alt=\"Website running on IPFS\"><\/p>\n<p>Alternatively, the website can be viewed through public IPFS Web Gateways such as the one run by <code class=\"\" data-line=\"\">ipfs.io<\/code>: <a href=\"https:\/\/ipfs.io\/ipfs\/QmeoPDguuLaYsZXnniL5Be7t9hmbU55XU6B8LE5cuS7TJr\/yummyfruits\/apple.html\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/ipfs.io\/ipfs\/QmeoPDguuLaYsZXnniL5Be7t9hmbU55XU6B8LE5cuS7TJr\/yummyfruits\/apple.html<\/a><\/p>\n<p>Feel free to share this link with your all friends so they know exactly what kind of fruits you are into.<\/p>\n<h5>Adding more fruits to our website<\/h5>\n<p>Showcasing only two fruits is quite pointless, so we add more:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ echo &#039;&lt;h1 style=&quot;color: darkred;&quot;&gt;Cherries are so sweet! \ud83c\udf52&lt;\/h1&gt;&#039; &gt; $SITEDIR\/cherry.html\n$ echo &#039;&lt;h1 style=&quot;color: brown;&quot;&gt;I only ever date dates! \ud83e\udd13&lt;\/h1&gt;&#039; &gt; $SITEDIR\/date.html\n$ ipfs add -r $WEBDIR\nadded QmUx6PWeRAPxCTqmK4eGq95KnvPCJwNhfU1cgw5RWCEG8y websites\/yummyfruits\/apple.html\nadded QmZkMyLFKAQqRXer1MExU3vnfAFmxCN7rFkpmsUQBG81UN websites\/yummyfruits\/banana.html\nadded QmVW36JGRo1Q4SjNjktWPnd9SF3SEynH7nfTu4KBF7HSBn websites\/yummyfruits\/cherry.html\nadded QmYdEHFhCX8U3zgGEfayTpfvM6fj21pedBmG7jbPUrio1B websites\/yummyfruits\/date.html\nadded QmTmtiurZozG98k7QnTvbQ2i95b3VPou3By8HQm8TRHEKq websites\/yummyfruits\nadded QmPHxSJX9VeFgkXoESJ29zLoCVh6GP1yCkb68fu97drSts websites\n261 B \/ 261 B [==========================================================================] 100.00%\n<\/code><\/pre>\n<p>But what&#8217;s that? The hash value for our website has changed!<\/p>\n<p>Recall that IPFS objects are addressed by their <em>content<\/em><sup id=\"fnref-9649-content_addressing\"><a href=\"#fn-9649-content_addressing\" class=\"jetpack-footnote\">6<\/a><\/sup> (to be precise, their hash) and not their <em>address<\/em>. Once we change a file or folder, its hash must change too. We didn&#8217;t touch <code class=\"\" data-line=\"\">apple.html<\/code> and <code class=\"\" data-line=\"\">banana.html<\/code> so their hash values remained the same. On the other hand, the hash of the <code class=\"\" data-line=\"\">yummyfruits<\/code> directory object was modified because two new files, <code class=\"\" data-line=\"\">cherry.html<\/code> and <code class=\"\" data-line=\"\">date.html<\/code>, were added to it. Consequently, as the hash for <code class=\"\" data-line=\"\">yummyfruits<\/code> changed, its parent&#8217;s hash must to change too. This chain of hash-updates recursively propagates to all parents of a modified object (child).<\/p>\n<p>Confirming that everything we just did really works:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ ipfs cat QmPHxSJX9VeFgkXoESJ29zLoCVh6GP1yCkb68fu97drSts\/yummyfruits\/{cherry,date}.html\n&lt;h1 style=&quot;color: darkred&quot;&gt;Cherries are so sweet! \ud83c\udf52&lt;\/h1&gt;\n&lt;h1 style=&quot;color: brown&quot;&gt;I only ever date dates! \ud83e\udd13&lt;\/h1&gt;\n# This on the other hand will not work as `cherry.html` is not available under the old resource hash\n$ ipfs cat QmeoPDguuLaYsZXnniL5Be7t9hmbU55XU6B8LE5cuS7TJr\/yummyfruits\/cherry.html\nError: no link named &quot;cherry.html&quot; under QmbVPuewTkDPSLJku69JFkAox3VUcMiokK2fk2HniT7e1u\n<\/code><\/pre>\n<p>This new hash also needs to be shared with your friends, otherwise they would never know you like cherries too.<\/p>\n<h5>Enter IPNS, the InterPlanetary Naming System<\/h5>\n<p>Takeaway from the previous section was that modifying content of an existing resource will produce a new hash under which it can then be accessed. Using an old hash will still return the old content.<\/p>\n<p>Distributing a new hash every time a resource has changed would be really cumbersome which is where <strong>IPNS<\/strong>, the InterPlanetary Naming System, comes in handy. Using it, a dynamic resource (such as a website which might change from time to time) can always be addressed by the same, <em>static reference<\/em>. This static reference is your peer ID, the hash of your peer&#8217;s public key.<\/p>\n<p><em>Note: each peer ID can only reference a single resource such as our YummyFruits website. Multiple peer IDs can be created to reference more than just one dynamic resource. See <code class=\"\" data-line=\"\">ipfs key gen --help<\/code>.<\/em><\/p>\n<h5>Publishing an IPNS name<\/h5>\n<p>To make our YummyFruits website available on our peer ID, use <code class=\"\" data-line=\"\">ipfs name publish<\/code> followed by the hash of the updated <code class=\"\" data-line=\"\">yummyfruits<\/code> object:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ ipfs name publish QmTmtiurZozG98k7QnTvbQ2i95b3VPou3By8HQm8TRHEKq\nPublished to QmXWf53PNW5nSrP2voZg9GHfYpqPWrYo7677saX6yFV8Z1: \/ipfs\/QmTmtiurZozG98k7QnTvbQ2i95b3VPou3By8HQm8TRHEKq\n#            ^ This is the peer ID\n<\/code><\/pre>\n<p>Using <code class=\"\" data-line=\"\">ipns name resolve<\/code> we can now resolve our <em>IPNS name<\/em> to the <em>IPFS object<\/em>:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ IPFS_PEER_ID=$(ipfs id --format=&#039;&lt;id&gt;\\n&#039;)\n$ echo $IPFS_PEER_ID\nQmXWf53PNW5nSrP2voZg9GHfYpqPWrYo7677saX6yFV8Z1&lt;\/id&gt;\n\n$ ipfs name resolve $IPFS_PEER_ID\n\/ipfs\/QmTmtiurZozG98k7QnTvbQ2i95b3VPou3By8HQm8TRHEKq\n<\/code><\/pre>\n<p>The website can now be accessed by our peer ID:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">#           v Note the use of IPNS here\n$ ipfs cat \/ipns\/$IPFS_PEER_ID\/cherry.html\n&lt;h1 style=&quot;color: darkred;&quot;&gt;Cherries are so sweet! \ud83c\udf52&lt;\/h1&gt;\n<\/code><\/pre>\n<h5>Adding even more yummy fruits<\/h5>\n<p>To better illustrate the usefulness of IPNS, let&#8217;s first add another one of our favorite fruits:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ echo &#039;&lt;h1 style=&quot;color: dark;&quot;&gt;Elderberries are nice but stain clothes, be careful! \u26a0\ufe0f&lt;\/h1&gt;&#039; &gt; $SITEDIR\/elder.html\n$ ipfs add -r $WEBDIR\nadded QmUx6PWeRAPxCTqmK4eGq95KnvPCJwNhfU1cgw5RWCEG8y websites\/yummyfruits\/apple.html\nadded QmZkMyLFKAQqRXer1MExU3vnfAFmxCN7rFkpmsUQBG81UN websites\/yummyfruits\/banana.html\nadded QmVW36JGRo1Q4SjNjktWPnd9SF3SEynH7nfTu4KBF7HSBn websites\/yummyfruits\/cherry.html\nadded QmYdEHFhCX8U3zgGEfayTpfvM6fj21pedBmG7jbPUrio1B websites\/yummyfruits\/date.html\nadded QmR9hSAqq425pWzvjtA8g6xEak1Ks96fE8kuLU4xDTQTCX websites\/yummyfruits\/elder.html\nadded QmV9z6Dccx9onMnggZ6YHtofAsAw9xWHMnzMbMVt4gobkr websites\/yummyfruits\nadded QmaX2HpHbf2VrAuenhFDYJibMNmk66ZTy4Y8Cq84WB4PwN websites\n351 B \/ 351 B [==========================================================================] 100.00%\n<\/code><\/pre>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\"># This doesn&#039;t work yet\u2026\n$ ipfs cat \/ipns\/$IPFS_PEER_ID\/elder.html\nError: no link named &quot;elder.html&quot; under QmTmtiurZozG98k7QnTvbQ2i95b3VPou3By8HQm8TRHEKq\n\n# \u2026 we need to re-publish the updated hash first \u2026\n$ ipfs name publish QmV9z6Dccx9onMnggZ6YHtofAsAw9xWHMnzMbMVt4gobkr\nPublished to QmXWf53PNW5nSrP2voZg9GHfYpqPWrYo7677saX6yFV8Z1: \/ipfs\/QmV9z6Dccx9onMnggZ6YHtofAsAw9xWHMnzMbMVt4gobkr\n\n# \u2026 now it does!\n$ ipfs cat \/ipns\/$IPFS_PEER_ID\/elder.html\n&lt;h1 style=&quot;color: dark;&quot;&gt;Elderberries are nice but stain clothes, be careful! \u26a0\ufe0f&lt;\/h1&gt;\n<\/code><\/pre>\n<p>As a last step, add an <code class=\"\" data-line=\"\">index.html<\/code> file with references to all our favorite fruits added so far:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ echo &#039;&lt;ul&gt;\n    &lt;li&gt;&lt;a href=&quot;apple.html&quot;&gt;Apple&lt;\/a&gt;&lt;\/li&gt;\n    &lt;li&gt;&lt;a href=&quot;banana.html&quot;&gt;Banana&lt;\/a&gt;&lt;\/li&gt;\n    &lt;li&gt;&lt;a href=&quot;cherry.html&quot;&gt;Cherry&lt;\/a&gt;&lt;\/li&gt;\n    &lt;li&gt;&lt;a href=&quot;date.html&quot;&gt;Date&lt;\/a&gt;&lt;\/li&gt;\n    &lt;li&gt;&lt;a href=&quot;elder.html&quot;&gt;Elder&lt;\/a&gt;&lt;\/li&gt;\n&lt;\/ul&gt;&#039; &gt; $SITEDIR\/index.html\n<\/code><\/pre>\n<p>Then add and re-publish:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ ipfs add -r $WEBDIR\nadded QmUx6PWeRAPxCTqmK4eGq95KnvPCJwNhfU1cgw5RWCEG8y websites\/yummyfruits\/apple.html\nadded QmZkMyLFKAQqRXer1MExU3vnfAFmxCN7rFkpmsUQBG81UN websites\/yummyfruits\/banana.html\nadded QmVW36JGRo1Q4SjNjktWPnd9SF3SEynH7nfTu4KBF7HSBn websites\/yummyfruits\/cherry.html\nadded QmYdEHFhCX8U3zgGEfayTpfvM6fj21pedBmG7jbPUrio1B websites\/yummyfruits\/date.html\nadded QmR9hSAqq425pWzvjtA8g6xEak1Ks96fE8kuLU4xDTQTCX websites\/yummyfruits\/elder.html\nadded QmTbcghvnaEVg86PzATNTYu6QgZ9V5iRv3Ss7BV81djSEb websites\/yummyfruits\/index.html\nadded QmNr95KriEevBHikGKR5JvPfnKckiQRyNEpkb8z7yqAHg4 websites\/yummyfruits\nadded QmPBkXse5gUKXakJW8JjP8ukVQ4LNN5X3Um91iHmcQCKnM websites\n574 B \/ 574 B [==========================================================================] 100.00%\n\n$ ipfs name publish QmNr95KriEevBHikGKR5JvPfnKckiQRyNEpkb8z7yqAHg4\nPublished to QmXWf53PNW5nSrP2voZg9GHfYpqPWrYo7677saX6yFV8Z1: \/ipfs\/QmNr95KriEevBHikGKR5JvPfnKckiQRyNEpkb8z7yqAHg4\n<\/code><\/pre>\n<p>Fire up a browser and access the IPNS via either the <a href=\"http:\/\/localhost:8080\/ipns\/QmXWf53PNW5nSrP2voZg9GHfYpqPWrYo7677saX6yFV8Z1\/\">local Web Gateway<\/a> or a public one, e.g. the one provided by <code class=\"\" data-line=\"\">ipfs.io<\/code>: <a href=\"https:\/\/ipfs.io\/ipns\/QmXWf53PNW5nSrP2voZg9GHfYpqPWrYo7677saX6yFV8Z1\/\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/ipfs.io\/ipns\/QmXWf53PNW5nSrP2voZg9GHfYpqPWrYo7677saX6yFV8Z1\/<\/a>.<\/p>\n<p><img decoding=\"async\" src=\"\/wp-content\/uploads\/2020\/02\/website-ipns.png\" alt=\"Website running with IPNS\"><\/p>\n<p>The only hash value you ever need to tell your friends from now on is the peer ID, <code class=\"\" data-line=\"\">QmXWf53PNW5nSrP2voZg9GHfYpqPWrYo7677saX6yFV8Z1<\/code>.<\/p>\n<p><em>Really? I am used to sharing nice-looking URLs with my friends!<\/em><\/p>\n<p>All IPFS resources are <em>self-authenticating<\/em>. This means when requesting for a resource by its hash at a (potentially malicious) node on the IPFS network, neither they nor anyone in between (a man-in-the-middle, <em>MITM<\/em>) can inject additional data to the requested object as it would result in a different hash.<\/p>\n<p>Simple illustration:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\"># A colleague creates an arbitrary file on IPFS and sends you the hash of it over an authenticated channel\n$ echo &#039;Hello World!&#039; | ipfs add -q\nQmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG\n<\/code><\/pre>\n<p>As you are not yet in possession of a resource with that hash, you begin querying other peers which you are connected to. Once a providing node has been found and the file transfer has completed, your (receiving) IPFS node computes the hash of whatever data the other node sent. If that hash doesn&#8217;t match the hash value you were initially given by your colleague, your node knows something suspicious happened and discards the data. IPFS then asks another node for the file. Nodes will be penalized<sup id=\"fnref-9649-penalty\"><a href=\"#fn-9649-penalty\" class=\"jetpack-footnote\">7<\/a><\/sup> locally when they frequently sent such junk data.<\/p>\n<p>To find nodes which provide the file, run the following command:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ ipfs dht findprovs QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG\n[..] long list of peers serving the file, an excerpt:\nQmXWf53PNW5nSrP2voZg9GHfYpqPWrYo7677saX6yFV8Z1\nQma5q8kiKopYw1G3sSTwtRXDgx1AQ7a7jgJy6gdERvvWEY\nQmdoy815C6fWDiUCeCq8ETVBwaWN2gsdmGTf9f4ST9P7X7\nQmV1gHWEBkfVDXRNbrUJ6qLfk9GiCR5gSbUGYdVXwzZCeK\n<\/code><\/pre>\n<h5>Creating beautiful<sup id=\"fnref-9649-beauty\"><a href=\"#fn-9649-beauty\" class=\"jetpack-footnote\">8<\/a><\/sup> IPNS names<\/h5>\n<p>Hashes are hard to remember. On the other hand, domain names as we use them every day can easily be remembered.<br \/>\nIPFS supports <a href=\"https:\/\/docs.ipfs.io\/guides\/concepts\/dnslink\/\">DNSLink<\/a>, a system allowing to map domain names to an IPFS address, creating memorable aliases of the objects referred to.<\/p>\n<p>For that, it uses DNS TXT records. To make an IPFS resource (e.g. our latest YummyFruits website, <code class=\"\" data-line=\"\">QmNr95KriEevBHikGKR5JvPfnKckiQRyNEpkb8z7yqAHg4<\/code>) available under <code class=\"\" data-line=\"\">\/ipns\/yummyfruits.ipfs.leonklingele.de\/<\/code>, simply create a TXT DNS record on the <code class=\"\" data-line=\"\">_dnslink.yummyfruits.ipfs<\/code> subdomain of your zone as follows:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">--- a\/zones\/leonklingele.de\n+++ b\/zones\/leonklingele.de\n@@ -2,3 +2,3 @@ $TTL     3600\n@       IN SOA  ns1.leonklingele.de. hostmaster.leonklingele.de. (\n-            2020022400    ; Serial number\n+            2020022500    ; Serial number\n1800          ; Refresh (30 minutes)\n@@ -424,2 +424,3 @@ www      IN A    185.183.159.234\n\n+_dnslink.yummyfruits.ipfs   IN TXT  &quot;dnslink=\/ipfs\/QmNr95KriEevBHikGKR5JvPfnKckiQRyNEpkb8z7yqAHg4&quot;<\/code><\/pre>\n<p>Alternatively, specify your IPNS name in the <code class=\"\" data-line=\"\">dnslink<\/code> directive.<\/p>\n<p>Confirm that it really works:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ dig +short -t TXT _dnslink.yummyfruits.ipfs.leonklingele.de\n&quot;dnslink=\/ipfs\/QmNr95KriEevBHikGKR5JvPfnKckiQRyNEpkb8z7yqAHg4&quot;\n\n$ ipfs dns yummyfruits.ipfs.leonklingele.de\n\/ipfs\/QmNr95KriEevBHikGKR5JvPfnKckiQRyNEpkb8z7yqAHg4\n\n$ ipfs cat \/ipfs\/QmNr95KriEevBHikGKR5JvPfnKckiQRyNEpkb8z7yqAHg4\/index.html\n&lt;ul&gt;\n    &lt;li&gt;&lt;a href=&quot;apple.html&quot;&gt;Apple&lt;\/a&gt;&lt;\/li&gt;\n    &lt;li&gt;&lt;a href=&quot;banana.html&quot;&gt;Banana&lt;\/a&gt;&lt;\/li&gt;\n    &lt;li&gt;&lt;a href=&quot;cherry.html&quot;&gt;Cherry&lt;\/a&gt;&lt;\/li&gt;\n    &lt;li&gt;&lt;a href=&quot;date.html&quot;&gt;Date&lt;\/a&gt;&lt;\/li&gt;\n    &lt;li&gt;&lt;a href=&quot;elder.html&quot;&gt;Elder&lt;\/a&gt;&lt;\/li&gt;\n&lt;\/ul&gt;\n# Or, using the IPNS name directly\n$ ipfs cat \/ipns\/yummyfruits.ipfs.leonklingele.de\/index.html\n[..] same as above\n\n# Yay!\n<\/code><\/pre>\n<p>In your favorite browser, head over to <a href=\"https:\/\/ipfs.io\/ipns\/yummyfruits.ipfs.leonklingele.de\/\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/ipfs.io\/ipns\/yummyfruits.ipfs.leonklingele.de\/<\/a> and view it in action. Beware that \u2014 as you are not <em>directly<\/em> accessing a hash here but only a DNS-resolved IPFS address \u2014 <em>MITM<\/em> attacks become a problem if the domain doesn&#8217;t employ additional security mechanisms such as DNSSEC to add authenticity to the returned TXT record. Also note that DNSLink uses DNS which is a <em>centralized<\/em> system that can break more easily \u2014 we wanted to get <em>decentralized<\/em> and <em>distributed<\/em> after all, <a href=\"#ipfs-the-interplanetary-file-system-demystified\">remember?<\/a><\/p>\n<p><img decoding=\"async\" src=\"\/wp-content\/uploads\/2020\/02\/website-ipns-domain.png\" alt=\"Website running with IPNS using a custom DNSLink domain\"><\/p>\n<h4>Dealing with IPFS blocks<\/h4>\n<p>So far, we&#8217;ve always dealt with IPFS <code class=\"\" data-line=\"\">objects<\/code>. Objects are blocks represented in a <a href=\"https:\/\/github.com\/ipld\/specs\/blob\/master\/block-layer\/codecs\/dag-pb.md\">Merkle Directed Acyclic Graph<\/a> (<em>DAG<\/em>) but are addtionally encoded in the <a href=\"https:\/\/github.com\/ipfs\/go-unixfs\/blob\/master\/pb\/unixfs.proto\">UnixFS protobuf<\/a> data format. Amongst other tasks, the <em>UnixFS<\/em> data format is responsible for encoding large files into multiple blocks. Files are first broken down into blocks and then arranged in a tree-like structure using <em>link nodes<\/em> to tie them together. A given file&#8217;s hash is actually the hash of the root (uppermost) node in the DAG. See <a href=\"https:\/\/docs.ipfs.io\/guides\/examples\/blocks\/\">Dealing with Blocks<\/a> for more details.<\/p>\n<p>Citing the <a href=\"https:\/\/github.com\/ipfs\/specs\/blob\/master\/MERKLE_DAG.md\">specs<\/a>, the IPFS Merkle DAG is a directed acyclic graph whose edges are Merkle-links which are cryptographic hashes of the targets embedded in the sources. This means that links to objects can authenticate the objects themselves, and that every object contains a secure representation of its children.<\/p>\n<p>If this format is not required, using blocks directly is recommended.<br \/>\nTo create a raw block, use the <code class=\"\" data-line=\"\">ipfs block<\/code> subcommand:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ echo &#039;This is a raw IPFS block!&#039; | ipfs block put\nQmZLxXbxEzCy8v9RBiCNuHKWYqgQJWMfGFJX78MAUZo5BX\n\n$ ipfs block get QmZLxXbxEzCy8v9RBiCNuHKWYqgQJWMfGFJX78MAUZo5BX\nThis is a raw IPFS block!\n\n# `ipfs cat` requires blocks be encoded in the UnixFS format, so `ipfs cat`&#039;ing\n# a non-UnixFS block will fail:\n$ ipfs cat QmZLxXbxEzCy8v9RBiCNuHKWYqgQJWMfGFJX78MAUZo5BX\nError: failed to decode Protocol Buffers: incorrectly formatted merkledag node: unmarshal failed. proto: PBNode: wiretype end group for non-group\n<\/code><\/pre>\n<h4>Dealing with raw IPFS objects<\/h4>\n<p>IPFS <code class=\"\" data-line=\"\">objects<\/code> can be explored with <code class=\"\" data-line=\"\">ipfs object<\/code>:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\"># No childs elements are linked as this object is leaf (file) in the DAG\n$ ipfs object get QmUx6PWeRAPxCTqmK4eGq95KnvPCJwNhfU1cgw5RWCEG8y | jq\n{\n  &quot;Links&quot;: [],\n  &quot;Data&quot;: &quot;\\b\\u0002\\u0012@&lt;h1 style=&quot;\\&quot;color:&quot; green;\\&quot;=&quot;&quot;&gt;I really like green Apples! \ud83c\udf4f&lt;\/h1&gt;\\n\\u0018@&quot;\n}\n\n# Modify the raw object and add it to IPFS\n$ ipfs object get QmUx6PWeRAPxCTqmK4eGq95KnvPCJwNhfU1cgw5RWCEG8y | sed &#039;s\/color: green\/color: orange\/&#039; | sed &#039;s\/green Apples\/big Oranges\/&#039; | sed &#039;s\/\ud83c\udf4f\/\ud83c\udf4a\/&#039; | ipfs object put\nadded QmPxnroqXbaFkZ2dkqwKKZDdLuxZQBGKE5x5TB7821FzPB\n\n$ ipfs cat QmPxnroqXbaFkZ2dkqwKKZDdLuxZQBGKE5x5TB7821FzPB\n&lt;h1 style=&quot;color: orange;&quot;&gt;I really like big Oranges! \ud83c\udf4a&lt;\/h1&gt;\n<\/code><\/pre>\n<p>Displaying objects which refer to a directory reveals the Merkle DAG structure:<\/p>\n<pre style=\"font-size: 13px\"><code class=\"\" data-line=\"\">$ ipfs object get QmNr95KriEevBHikGKR5JvPfnKckiQRyNEpkb8z7yqAHg4 | jq\n{\n  &quot;Links&quot;: [\n    {\n      &quot;Name&quot;: &quot;apple.html&quot;,\n      &quot;Hash&quot;: &quot;QmUx6PWeRAPxCTqmK4eGq95KnvPCJwNhfU1cgw5RWCEG8y&quot;,\n      &quot;Size&quot;: 72\n    },\n    {\n      &quot;Name&quot;: &quot;banana.html&quot;,\n      &quot;Hash&quot;: &quot;QmZkMyLFKAQqRXer1MExU3vnfAFmxCN7rFkpmsUQBG81UN&quot;,\n      &quot;Size&quot;: 84\n    },\n    {\n      &quot;Name&quot;: &quot;cherry.html&quot;,\n      &quot;Hash&quot;: &quot;QmVW36JGRo1Q4SjNjktWPnd9SF3SEynH7nfTu4KBF7HSBn&quot;,\n      &quot;Size&quot;: 69\n    },\n    {\n      &quot;Name&quot;: &quot;date.html&quot;,\n      &quot;Hash&quot;: &quot;QmYdEHFhCX8U3zgGEfayTpfvM6fj21pedBmG7jbPUrio1B&quot;,\n      &quot;Size&quot;: 68\n    },\n    {\n      &quot;Name&quot;: &quot;elder.html&quot;,\n      &quot;Hash&quot;: &quot;QmR9hSAqq425pWzvjtA8g6xEak1Ks96fE8kuLU4xDTQTCX&quot;,\n      &quot;Size&quot;: 98\n    },\n    {\n      &quot;Name&quot;: &quot;index.html&quot;,\n      &quot;Hash&quot;: &quot;QmTbcghvnaEVg86PzATNTYu6QgZ9V5iRv3Ss7BV81djSEb&quot;,\n      &quot;Size&quot;: 234\n    }\n  ],\n  &quot;Data&quot;: &quot;\\b\\u0001&quot;\n}<\/code><\/pre>\n<h2>Conclusion<\/h2>\n<p>This was our small introduction to <code class=\"\" data-line=\"\">ipfs<\/code> and some details of the inner workings of the IPFS protocol. If you&#8217;re interested, please don&#8217;t hesitate to read more about it in the <a href=\"#references-further-reading\">referenced articles<\/a>.<\/p>\n<h2>References &amp; further reading<\/h2>\n<ul>\n<li>The <a href=\"https:\/\/ipfs.io\/\">IPFS website<\/a><\/li>\n<li>The <a href=\"https:\/\/github.com\/ipfs\/papers\/raw\/master\/ipfs-cap2pfs\/ipfs-p2p-file-system.pdf\">IPFS whitepaper<\/a><\/li>\n<li>The <a href=\"https:\/\/docs.ipfs.io\/\">IPFS documentation<\/a><\/li>\n<li>The <a href=\"https:\/\/github.com\/ipfs\/specs\">IPFS specifications<\/a><\/li>\n<li>The <a href=\"https:\/\/github.com\/ipld\/specs\">IPLD specifications<\/a><\/li>\n<li>The <a href=\"https:\/\/github.com\/ipfs\/go-ipfs\">IPFS implementation <code class=\"\" data-line=\"\">ipfs<\/code> written in Go<\/a><\/li>\n<li>The <a href=\"https:\/\/github.com\/ipfs\/logo\">IPFS logo<\/a><\/li>\n<li>IPFS&#8217; <a href=\"https:\/\/dweb-primer.ipfs.io\/\">Decentralized Web Primer<\/a><\/li>\n<li>The <a href=\"https:\/\/proto.school\/\">Interactive IPFS Tutorials<\/a><\/li>\n<li>Carson Farmer&#8217;s series of <a href=\"https:\/\/medium.com\/@carsonfarmer\">posts on IPFS<\/a><\/li>\n<li><a href=\"https:\/\/cluster.ipfs.io\/\">IPFS Cluster<\/a> for automated data availability and<br \/>\nredundancy<\/li>\n<li><code class=\"\" data-line=\"\">ipfs<\/code>&#8216; <a href=\"https:\/\/docs.ipfs.io\/introduction\/usage\/#web-console\">Web UI<\/a><\/li>\n<li><a href=\"https:\/\/d.tube\/\">DTube<\/a>, a video platform and alternative to YouTube which<br \/>\nuses the IPFS network to host its videos<\/li>\n<\/ul>\n<div class=\"footnotes\">\n<hr>\n<ol>\n<li id=\"fn-9649-internet\">\n<em>Note<\/em>: Internet != Web, the <em>world wide web<\/em> is a service on the Internet, just as email, BitTorrent, IRC, SSH, etc.&nbsp;<a href=\"#fnref-9649-internet\">\u21a9<\/a><\/li>\n<li id=\"fn-9649-failures\">\nNot resilient to all kinds of failures, for sure\u2026&nbsp;<a href=\"#fnref-9649-failures\">\u21a9<\/a><\/li>\n<li id=\"fn-9649-content_hash\">\nBy the hash<sup id=\"fnref2:9649-hash\"><a href=\"#fn-9649-hash\" class=\"jetpack-footnote\">5<\/a><\/sup> of the content.&nbsp;<a href=\"#fnref-9649-content_hash\">\u21a9<\/a> <a href=\"9649-content_hash\">\u21a9<\/a><\/li>\n<li id=\"fn-9649-origin\">\nThis is where IPFS got its name from. See <a href=\"https:\/\/docs.ipfs.io\/introduction\/overview\/\">What is IPFS?<\/a>.&nbsp;<a href=\"#fnref-9649-origin\">\u21a9<\/a><\/li>\n<li id=\"fn-9649-hash\">\nThe hash in fact is a <a href=\"https:\/\/docs.ipfs.io\/guides\/concepts\/cid\/\">base58-encoded multihash<\/a>, also known as the content identifier (<em>CID<\/em>), from now on always referred to by <em>hash<\/em>. The hash function used to produce a hash is stored in the multihash alongside the hash value itself. This allows to switch hashing functions without breaking backwards-compatibility.&nbsp;<a href=\"#fnref-9649-hash\">\u21a9<\/a> <a href=\"9649-hash\">\u21a9<\/a><\/li>\n<li id=\"fn-9649-content_addressing\">\nThis is commonly referred to as <em>content-based addressing<\/em>.&nbsp;<a href=\"#fnref-9649-content_addressing\">\u21a9<\/a><\/li>\n<li id=\"fn-9649-penalty\">\nSee the <a href=\"https:\/\/github.com\/ipfs\/specs\/blob\/master\/BITSWAP.md\">BitSwap \/ ledger specification<\/a>.&nbsp;<a href=\"#fnref-9649-penalty\">\u21a9<\/a><\/li>\n<li id=\"fn-9649-beauty\">\nBeauty is in the eye of the beholder.&nbsp;<a href=\"#fnref-9649-beauty\">\u21a9<\/a><\/li>\n<\/ol>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>In this article we will explore IPFS, the InterPlanetary File System. IPFS is a system for storing and accessing files, websites and other kinds of data \u2014 just as the Web we enjoy using every day \u2014 but unlike the Web, IPFS is peer-to-peer based and automatically distributes its content across the network.<\/p>\n","protected":false},"author":961,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1,21,651,223],"tags":[335,329,328,327,332,326,349,334,330,331],"ppma_author":[808],"class_list":["post-9649","post","type-post","status-publish","format-standard","hentry","category-allgemein","category-system-architecture","category-system-designs","category-ultra-large-scale-systems","tag-cli","tag-decentralized","tag-distributed","tag-filesystem","tag-go","tag-ipfs","tag-ipns","tag-software","tag-web","tag-www"],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":22183,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/08\/26\/is-the-future-of-social-networks-decentralized\/","url_meta":{"origin":9649,"position":0},"title":"Is the future of social networks decentralized?","author":"Niklas Janssen","date":"26. August 2022","format":false,"excerpt":"Current social networks like Facebook, Twitter or Instagram mostly have a centralized approach ([1], [2], [6]). They are centralized in the sense, that all data is processed in data centers that are under a corporation's control. It is hard to beat the economies of scale that can be achieved by\u2026","rel":"","context":"In &quot;Rich Media Systems&quot;","block_context":{"text":"Rich Media Systems","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/interactive-media\/rich-media-systems\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/08\/p2p_structure.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/08\/p2p_structure.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/08\/p2p_structure.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/08\/p2p_structure.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/08\/p2p_structure.png?resize=1050%2C600&ssl=1 3x"},"classes":[]},{"id":2157,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2017\/03\/09\/of-apache-spark-hadoop-vagrant-virtualbox-and-ibm-bluemix-services-part-4-big-data-engineering\/","url_meta":{"origin":9649,"position":1},"title":"Of Apache Spark, Hadoop, Vagrant, VirtualBox and IBM Bluemix Services &#8211; Part 4 &#8211; Big Data Engineering","author":"bh051, cz022, ds168","date":"9. March 2017","format":false,"excerpt":"Our objective in this project was to build an environment that could be practical. So we set up a virtual Hadoop test cluster with virtual machines. Our production environment was a Hadoop Cluster in the IBM Bluemix cloud which we could use for free with our student accounts. We developed\u2026","rel":"","context":"In &quot;Student Projects&quot;","block_context":{"text":"Student Projects","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/student-projects\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/03\/dev-env-spark-768x512.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/03\/dev-env-spark-768x512.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/03\/dev-env-spark-768x512.png?resize=525%2C300&ssl=1 1.5x"},"classes":[]},{"id":26160,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2024\/02\/29\/why-system-monitoring-is-important-and-how-we-approached-it\/","url_meta":{"origin":9649,"position":2},"title":"Why system monitoring is important and how we approached it","author":"Michelle Becher","date":"29. February 2024","format":false,"excerpt":"Introduction Imagine building a service that aims to generate as much user traffic as possible to be as profitable as possible. The infrastructure of your service usually includes some kind of backend, a server and other frameworks. One day, something is not working as it should and you can't seem\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/02\/monitoring.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/02\/monitoring.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/02\/monitoring.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/02\/monitoring.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":664,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2016\/06\/24\/web-app-file-upload-vulnerabilities\/","url_meta":{"origin":9649,"position":3},"title":"Web App \u2013 File Upload Vulnerabilities","author":"Thomas Derleth","date":"24. June 2016","format":false,"excerpt":"Today we will discuss file upload vulnerabilities; a topic that is widely underestimated by developers. First, we will imagine a website in which it is possible to upload images with the format .jpg, .png, .gif and so on.\u00a0If an application does not have proper form validation for file uploads, an\u2026","rel":"","context":"In &quot;Interactive Media&quot;","block_context":{"text":"Interactive Media","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/interactive-media\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":902,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2016\/07\/22\/defense-in-depth-a-present-time-example\/","url_meta":{"origin":9649,"position":4},"title":"Defense in Depth: a present time example","author":"Benjamin Binder","date":"22. July 2016","format":false,"excerpt":"In this post, we want to take a look on the concept of defense in depth. Therefore we are going to examine Chrome OS, the niche operation system for web users.","rel":"","context":"In &quot;Secure Systems&quot;","block_context":{"text":"Secure Systems","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/system-designs\/secure-systems\/"},"img":{"alt_text":"Dark castle walls reaching in the sky","src":"https:\/\/upload.wikimedia.org\/wikipedia\/commons\/3\/32\/Caernarfon_Castle_Walls.jpg","width":350,"height":200,"srcset":"https:\/\/upload.wikimedia.org\/wikipedia\/commons\/3\/32\/Caernarfon_Castle_Walls.jpg 1x, https:\/\/upload.wikimedia.org\/wikipedia\/commons\/3\/32\/Caernarfon_Castle_Walls.jpg 1.5x, https:\/\/upload.wikimedia.org\/wikipedia\/commons\/3\/32\/Caernarfon_Castle_Walls.jpg 2x, https:\/\/upload.wikimedia.org\/wikipedia\/commons\/3\/32\/Caernarfon_Castle_Walls.jpg 3x, https:\/\/upload.wikimedia.org\/wikipedia\/commons\/3\/32\/Caernarfon_Castle_Walls.jpg 4x"},"classes":[]},{"id":2151,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2017\/03\/08\/of-apache-spark-hadoop-vagrant-virtualbox-and-ibm-bluemix-services-part-2-apache-hadoop-ecosystem\/","url_meta":{"origin":9649,"position":5},"title":"Of Apache Spark, Hadoop, Vagrant, VirtualBox and IBM Bluemix Services &#8211; Part 2 &#8211; Apache Hadoop Ecosystem","author":"bh051, cz022, ds168","date":"8. March 2017","format":false,"excerpt":"In our project we primarily implemented Spark applications, but we used components of Apache Hadoop like the Hadoop distributed file system or the cluster manager Hadoop YARN. For our discussion in the last part of this blog article it is moreover necessary to understand Hadoop MapReduce for comparison to Apache\u2026","rel":"","context":"In &quot;Student Projects&quot;","block_context":{"text":"Student Projects","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/student-projects\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"jetpack_sharing_enabled":true,"authors":[{"term_id":808,"user_id":961,"is_guest":0,"slug":"lk163","display_name":"Leon Klingele","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/1f0b9e6e47bd4b8d164510c4e7cdcdd346a8dc16f447bac78cbc44ce876d4d72?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/9649","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/users\/961"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/comments?post=9649"}],"version-history":[{"count":48,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/9649\/revisions"}],"predecessor-version":[{"id":25419,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/9649\/revisions\/25419"}],"wp:attachment":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/media?parent=9649"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/categories?post=9649"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/tags?post=9649"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/ppma_author?post=9649"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}