/ HOWTO_IPFS.md
HOWTO_IPFS.md
  1  # Static Webpages on IPFS and IPNS
  2  
  3  demo webpage in `./web-static`
  4  
  5  ## deploy a static web page to IPFS
  6  
  7  ```bash
  8  ipfs add -r ./web-static --pin
  9  # added QmWYZ65vwQLrqRWKhYL86kHrJwt1d7bJzmGDSvJZ8wX9Ha web-static/index.html
 10  # added QmTe4hAzoc2vS6ikp8TENnxZY3at1fzkv54Jh2LNPCHEJD web-static/skunk.jpg
 11  # added QmU2JQChEeGJaX3jqABaWaTBZ37EX5oJAMaNxqmGVJmcUA web-static
 12  ```
 13  
 14  That will already be enough for some browsers (e.g. brave) to resolve the content using IPFS.
 15  
 16  [ipfs://QmU2JQChEeGJaX3jqABaWaTBZ37EX5oJAMaNxqmGVJmcUA](ipfs://QmU2JQChEeGJaX3jqABaWaTBZ37EX5oJAMaNxqmGVJmcUA)
 17  
 18  many browsers, especially on mobile, need a gateway url:
 19  https://ipfs.io/ipfs/QmU2JQChEeGJaX3jqABaWaTBZ37EX5oJAMaNxqmGVJmcUA
 20  
 21  ## improve availability
 22  
 23  IPFS routing is very unreliable, so we may want to use a pinning service like infura and pinata in addition to our own ipfs nodes.
 24  
 25  ### pin on self-hosted mirror node
 26  
 27  connect your local node to the remote one explicitly (not mandatory, but improves success rate)
 28  ```bash
 29  ipfs swarm connect /ip4/129.212.213.82/tcp/4001/p2p/12D3KooWCtS4Li5YJhjj2fWWKxgqZi7t6FReuVtrN9MRQtLrg7Tj
 30  ```
 31  
 32  then pin the content:
 33  
 34  ```bash
 35  curl -u <USER>:<PWD> -s -X POST "https://ipfs2-api.encointer.org/api/v0/pin/add?recursive=true&arg=QmU2JQChEeGJaX3jqABaWaTBZ37EX5oJAMaNxqmGVJmcUA"
 36  ```
 37  
 38  ### pin with CRUST
 39  
 40  See [HOWTO_CRUST.md](HOWTO_CRUST.md). Not sure how long they will survive, but: For public files, CRUST is a good and cheap upload & pinning solution with multiple replicas. No need for email: Login with crust wallet 
 41  
 42  ## publish to ENS
 43  
 44  See [HOWTO-ENS.md](HOWTO-ENS.md)
 45  
 46  ## publish an IPNS address
 47  
 48  **DISCOURAGED!** see [multiparty-control](#multiparty-control) below for why
 49  
 50  For reference, here's how to do it anyway:
 51  
 52  Create a new named owner key for IPNS publishing:
 53  
 54  ```bash
 55  ipfs key gen --type ed25519 --ipns-base b58mh skunk-funk
 56  # 12D3KooWJiYyq1qRNCoTELgeqV84Yko27ZV2adcqsTN15SZSHryk
 57  ```
 58  
 59  Publish the IPNS address:
 60  
 61  ```bash
 62  ipfs name publish --key skunk-funk QmU2JQChEeGJaX3jqABaWaTBZ37EX5oJAMaNxqmGVJmcUA
 63  # Published to k51qzi5uqu5djh8k8yfnjs21pe4sgqkorynz26b3y5g72pjrfuumjlsgio7mkv: /ipfs/QmU2JQChEeGJaX3jqABaWaTBZ37EX5oJAMaNxqmGVJmcUA
 64  ```
 65  
 66  Now you can visit the webpage using the IPNS address:
 67  
 68  https://ipfs.io/ipns/k51qzi5uqu5djh8k8yfnjs21pe4sgqkorynz26b3y5g72pjrfuumjlsgio7mkv
 69  
 70  ### update the webpage
 71  
 72  Only the owner key can update the IPNS target.
 73  
 74  1. edit
 75  2. ipfs add
 76  3. call ipfs name publish again with the new IPFS CID
 77  
 78  ### multiparty control
 79  
 80  We don't like single-party ownership. Our requirement is
 81  * any curator can update the webpage, if possible applying a threshold of 2 curators
 82  * the curator set should be able to change
 83  
 84  **There is currently no out-of-the-box solution. use ENS in combination with SAFE instead!** 
 85  
 86  #### best: threshold multisig
 87  
 88  [tracking issue to add multisig support to IPNS](https://github.com/ipfs/specs/issues/416)
 89  
 90  also includes a PoC on Go
 91  
 92  No solution for changing signatories in the future yet. Use ENS instead
 93  
 94  ### simple: share the owner key with all curators
 95  
 96  Not very secure, but straightforward: share the owner key file with all curators. Any curator can then update the webpage.
 97  
 98  ```bash
 99  ipfs key export skunk-funk
100  # will dump the key to `./skunk-funk.key`
101  ```
102  
103  Share that keyfile with all other members securely. They import it with 
104  
105  ```bash
106  ipfs key import skunk-funk ./skunk-funk.key
107  ```
108  
109  #### slightly more secure: Shamir's secret sharing
110  
111  One curator creates the key and splits the secret into N shares, of which M are necessary to recover the secret
112  
113  * linux cli tool: `ssss-split` and `ssss-combine`
114  
115  ## light cluster of IPFS nodes
116  
117  Goal: follow whatever your friends are pinning and mirror these pins. No strict cluster coupling, no consensus. Just publish your pins to IPNS for your friends to consume and mirror if they want to. This is a very lightweight way to increase availability of our content without relying on a third party pinning service.
118  
119  create/edit your config in `./pin-follower/config.json`:
120  ```json
121  {
122    "ipfsApi": "http://localhost:5001",
123    "publishIntervalSeconds": 1800,
124    "syncIntervalSeconds": 3600,
125    "ipnsLifetime": "24h",
126    "resolveTimeout": "60s",
127    "stateDir": "~/.pin-follower",
128    "friends": [
129      { "name": "encointer-do", "ipnsKey": "12D3KooWCtS4Li5YJhjj2fWWKxgqZi7t6FReuVtrN9MRQtLrg7Tj" }
130    ]
131  }
132  ```
133  
134  ```bash
135  ./scripts/pin-follower.sh --once
136  #2026-02-24T11:56:12Z [pin-follower] Waiting for IPFS API at http://localhost:5001 ...
137  #2026-02-24T11:56:12Z [pin-follower] IPFS API is ready (PeerID: 12D3KooWNf2yMKjkgLkqcBcfAzb5jiHn5zZxG3EPUJQuhpbDA8n2)
138  #2026-02-24T11:56:12Z [pin-follower] Share this with friends: {"name":"<yourname>","ipnsKey":"12D3KooWNf2yMKjkgLkqcBcfAzb5jiHn5zZxG3EPUJQuhpbDA8n2"}
139  #2026-02-24T11:56:12Z [pin-follower] Running once
140  #2026-02-24T11:56:12Z [pin-follower] Publish: collecting pins...
141  #2026-02-24T11:56:13Z [pin-follower] Publish: 26 own pins (excluding friend pins)
142  #2026-02-24T11:56:13Z [pin-follower] Publish: pin list CID=QmeBbCVLADyEiKH9yq12XTwbFeESMhLzjFsAA2A25Hw9aC, publishing to IPNS (this can take minutes)...
143  #2026-02-24T11:56:31Z [pin-follower] Publish: done, IPNS=k51qzi5uqu5dkxr7ol779xt3ygop35pg82fzbl2l56qeaadc2l2dv0l2ar0uhj, CID=QmeBbCVLADyEiKH9yq12XTwbFeESMhLzjFsAA2A25Hw9aC, pins=26
144  #2026-02-24T11:56:31Z [pin-follower] Sync: processing 1 friend(s)...
145  #2026-02-24T11:56:31Z [pin-follower] Sync [encointer-do]: resolving 12D3KooWCtS4Li5YJhjj2fWWKxgqZi7t6FReuVtrN9MRQtLrg7Tj ...
146  #2026-02-24T11:56:36Z [pin-follower] Sync [encointer-do]: resolved to /ipfs/QmWHYL72unWXhZWuY7zxUvVuRUSXg3yrQmwjjaU6HJ69Na
147  #2026-02-24T11:56:39Z [pin-follower] Sync [encointer-do]: pinning 1 new CIDs...
148  #2026-02-24T11:56:39Z [pin-follower] Sync [encointer-do]: pinned QmRWzCGwYZ3WPGdBdkSzWfHX9Q2MGnYKVYYZrBDqED4muA
149  #2026-02-24T11:56:39Z [pin-follower] Sync [encointer-do]: done (new=1, stale=0)
150  #2026-02-24T11:56:40Z [pin-follower] Done
151  ```
152  
153  Install as cronjob
154  ```bash
155  crontab -e
156  # sync pins once per hour
157  0 * * * *  CONFIG_FILE=$HOME/.pin-follower/config.json $HOME/pin-follower.sh --once >> $HOME/.pin-follower/cron.log 2>&1  
158  ```