Publish a website to IPFS that can be updated without breaking all the URLs. In more technical terms: create a mutable IPFS website with a stable IPNS hash. Using this method, a website built with relative URLs will function the same on IPFS as on the classic web.
git clone https://github.com/0xidm/mutable-ipfs-website new-website
cd new-website
cp settings.example.mk settings.mk
# change IPFS_API in settings.mk if IPFS not on localhost
make key build publish
You now have a website with a URL like https://ipfs.io/ipns/$HASH
Make some changes to index.md
, then update the website:
make build publish
Even though the content has changed, the URL remains stable.
One unique feature of IPFS is that it is immutable. When content is changed on IPFS, this produces a new URL to reference the new content. For content like a website, it's desirable to have a URL that stays the same - and the trouble with IPFS occurs when the website needs to be updated.
IPNS provides a Name System that can solve the problem of ever-changing IPFS URLs. When IPFS content is updated, IPNS can be updated to point to the new content. This IPNS hash never changes; it is stable over time. On its own, IPNS is a complete solution for single-page websites.
What if you needed to publish many files at once, like a regular website? MFS is a Mutable File System that provides a familiar path, subdirectory, and file metaphor on top of IPFS. With MFS, it's possible to publish and update a whole hierarchy of files and folders, just like a regular website. MFS provides a natural bridge between HTTP, HTML, and IPFS so that URLs can contain a file path - like normal.
This method for publishing via IPFS+IPNS+MFS+HTTP enables a classic website built for IPv4+HTTP (with relative URLs) to be published without modification on IPFS.
Rendering markdown to html requires pandoc. Try the following to install:
- macos:
brew install pandoc
- ubuntu/debian:
apt install pandoc
- redhat:
rpm install pandoc
Clone into the new-website
path and remove the existing git repo.
git clone https://github.com/0xidm/mutable-ipfs-website new-website
cd new-website
rm -rf .git
Copy settings.example.mk
to settings.mk
.
The following variables can be changed:
IPFS_API
: the IP v4 address and TCP port of your IPFS node. Default is/ip4/127.0.0.1/tcp/5001
IPFS_KEY
: name of IPFS key; also used as MFS path. Default iswebsite-1
STYLE
: a pandoc style used whenindex.md
is rendered toindex.html
Edit settings.mk
to specify a key name, then generate that key:
make key
which expands into:
ipfs --api=/ip4/127.0.0.1/tcp/5001 key gen website-1
It's easier to just make key
.
Modify index.md
, then render/deploy using the Makefile
make all
Now the website is available from a stable URL based on website-1
key.
make all
is equivalent to:
make build publish
which expands into the following:
./bin/build-pandoc.sh default
./bin/add-ipfs.sh -k website-1 -f _site/index.html
These commands can be invoked manually.
Add any file to IPFS, then publish to the IPNS hash from the website-1
key.
echo "Hello IPFS" > hello.txt
./bin/add-ipfs.sh -k website-1 -f hello.txt
This file is now available from a stable URL:
https://ipfs.io/ipns/$HASH/hello.txt
The file can be updated by re-running add-ipfs.sh
and the URL will remain stable, even though the file content has changed.
A file can be referenced from index.md
using a relative path.
In the hello.txt
example, specify URL in markdown as [a link to the text file](hello.txt)
A file can be placed in an MFS subdirectory.
In the following example, hello.txt
is placed into the documents
subdirectory:
echo "Hello IPFS" > hello.txt
./bin/add-ipfs.sh -k website-1 -d documents -f hello.txt
The file is now available inside the documents
subdirectory:
https://ipfs.io/ipns/$HASH/documents/hello.txt
Currently just 1-level of subdirectory nesting is supported. For more complex trees, consider the IPFS Web UI.
The IPFS web UI is an easy way to manage files for a website.
Using the Files interface, navigate to /public/$YOUR_KEY
to see the website files.
Any time files are changed, including when the IPFS Web UI changes files, IPNS must be updated. Sometimes IPNS must be manually refreshed.
The following will refresh IPNS for the configured key:
make refresh-ipns
which expands into:
./bin/add-ipfs.sh -k website-1 -p
The -p
option for add-ipfs.sh
causes IPNS to be published (updated).
Pandoc is used to render index.md
into index.html
.
Create a template, then modify as you like:
pandoc -D html > styles/template.html
- select a style from
./styles
- set STYLE in
settings.mk
- invoke
make build
Install new pandoc CSS styles by placing them into ./styles
.
The default
style can be replaced with a dark theme called water, by Kognise.
./bin/build-pandoc.sh water
To make this style permanent, set STYLE=water
in settings.mk
.
Discover vanity IPNS hashes with brute-force:
go install github.com/0xidm/peer-id-generator
peer-id-generator yourvanitystring
ipfs key import vanity-1 $HASH
Now you have a key called vanity-1
that produces a /ipns/$HASH
that you like.
To view your IPNS hash again:
ipfs key list -l --ipns-base b58mh
The 0xidm fork modifies the original code by meehow to reserve 1 CPU for system usability.
If you want to brute-force even faster, or if you have only 1 CPU, install their code: go install github.com/meehow/peer-id-generator
.
Create a new local git repo for the site.
git init --initial-branch=main
git add .
git commit -m "Initial commit"
Create a remote git repo (e.g. on github) and update your git remote to reference the new repo.
git remote add origin git@github.com:0xidm/new-website
git push -u origin main