The Problem

Traditional websites require servers, hosting, and ongoing maintenance. They can go down, get censored, or simply disappear when someone stops paying the bills.

The Solution

Store the entire website as transaction calldata on Ethereum. The data becomes immutable and permanent - as long as Ethereum exists, so does your content.

Why Calldata?

Calldata is the cheapest way to store data on Ethereum. It's included in every transaction and is permanently stored by all archive nodes. Perfect for small HTML/CSS/JS files.

How It Works

User visits your domain

First visit to lespiss.com loads a simple HTML page that registers a service worker.

Service worker installs

The browser caches sw.js locally. This runs in the background and intercepts all future requests.

User visits a route

When visiting lespiss.com/808, the service worker intercepts the request before it hits any server.

Fetch from Ethereum

The service worker calls the Etherscan API to fetch the transaction calldata for the mapped tx hash.

Decode and serve

Calldata is decoded from hex -> base64 -> HTML and served directly to the browser. No server involved.

The Code

The service worker fetches routes from an inscribed manifest:

// Fetch manifest from your wallet's ethscriptions
async function getRoutes() {
  const res = await fetch(
    `https://api.ethscriptions.com/v2/ethscriptions?creator=${WALLET}`
  );
  for (const eth of data.result) {
    if (eth.content_uri.includes('{"lespiss":')) {
      return JSON.parse(content).lespiss;
    }
  }
}

self.addEventListener('fetch', event => {
  const path = new URL(event.request.url).pathname.slice(1);
  if (routes[path]) {
    event.respondWith(serveFromEthereum(routes[path]));
  }
});

Inscribing Content

To inscribe HTML as calldata:

  1. Encode your HTML as base64
  2. Prepend with data:text/html;base64,
  3. Convert the entire string to hex
  4. Send as calldata in an Ethereum transaction (to yourself is fine)
  5. Note the tx hash and add it to your routes

Updating Content

Since calldata is immutable, you can't edit inscribed content. Instead:

  1. Inscribe new content with a new tx
  2. Inscribe a new manifest with updated routes
  3. Old versions remain accessible via direct tx hash

This creates a permanent version history. Every version of your site lives forever on Ethereum. No code deploys needed - just inscribe.

How Routes Work

The service worker loads routes from an inscribed manifest - a JSON ethscription that maps route names to tx hashes. When you visit /how, sw.js looks up the tx hash in the manifest and fetches that inscription.

// Manifest ethscription (inscribe to update routes)
{"lespiss":{
  "how": "0x97d1...e916",
  "previous": "0xbd10...2363"
}}

The sw.js scans your wallet's ethscriptions for the latest manifest (identified by {"lespiss": prefix). Inscribe a new manifest to update routes - no code push needed.

Direct tx hash links (/0xabc123...) always load that specific inscription - perfect for version history archives.

What About Content?

Logos and images can be inscribed and load from separate calldata. Our chain logo does exactly this - inscribe once, use infinite times. Click it to see the ethscription.

You can also link to CDNs and external images, libraries, and other websites - but nothing ensures those links won't die. Onchain content is permanent.

Setting Up the Entry Point

The index.html registers the service worker, and sw.js fetches the manifest and routes requests to Ethereum calldata. Deploy once - updates happen via inscribed manifests. We use Cloudflare Pages for the entry point (any static host works - Vercel, Netlify, GitHub Pages - but CF is free with instant SSL).

Create a GitHub repo

Push your index.html, sw.js, and static assets to GitHub.

Connect to Cloudflare Pages

Go to dash.cloudflare.com -> Workers & Pages -> Create -> Pages -> Connect to Git.

Deploy

Leave build settings empty (static site). Click Deploy. You get a *.pages.dev URL instantly.

Add custom domain

In Pages project -> Custom Domains -> Add. If using Cloudflare DNS, it configures automatically with SSL.

Why Cloudflare?

Free tier, global CDN, instant SSL, auto-deploys on git push. Service workers require HTTPS - Cloudflare handles this automatically.

Cost

Inscribing content costs gas, but it's surprisingly cheap for permanent, censorship-resistant hosting:

All hosted forever on Ethereum. No recurring fees, no renewal, no servers to maintain.

Source Code

The complete implementation is open source:

github.com/jefdiesel/lesspiss ->