Rethinking Smallweb Routing
by Achille Lacoin
4 min read
Smallweb v0.8.0
was released yesterday, and it included the first smallweb
breaking change.
The
~/www
convention was dropped, the defaut folder is now~/smallweb
In addition to this change, the folder should now be named after the hostname:
- example.smallweb.run => ~/smallweb/example.smallweb.run/
- pomdtr.me => ~/smallweb/pomdtr.me/
- example.localhost => ~/smallweb/example.localhost/
This change was not really well received:
I'm not a fan of the new hostname folder convention. It feels noisy.
I'm also a bit frustrated by this change, and this is my main gripe with it too. And this "ugliness" is (for me) exacerbated by the fact that there's going to be a lot of repetition if all my smallweb apps are
<app>.localhost
. I would prefer a convention like~/smallweb/localhost/example
mapping toexample.localhost
In this post, I'll try to address:
- the drawbacks of the previous convention
- the options I've considered
Why a change was needed
The smallweb routing system was originally designed for a single usecase:
hosting a unlimited amount of websites locally, using *.localhost
domains.
The convention was to:
- store all of your website in the smallweb root (
~/www
by default) - use the folder name has the subdomain
So ~/www/example/
would be mapped to https://example.localhost
.
As the project expanded, new usecases emerged for smallweb: hosting smallweb on a raspberrypi, or even on a VPS from hetzner/digital ocean...
And the intitial design hold quite well with these usecases. You would just
assign a domain to your device (ex: *.pomdtr.me
), and ~/www/example/
would
map to https://example.pomdtr.me
.
But what if I wanted to assign multiple domains to a single machine ? If I route
both *.pomdtr.me
and *.smallweb.run
to my machine, ~/www/example
will
match both https://example.pomdtr.me
and https://example.smallweb.run
. This
is probably not what the user want in most cases.
Options I've considered
Let's say we want to manage the following websites using smallweb.
https://smallweb.run
https://readme.smallweb.run
https://assets.smallweb.run
https://pomdtr.me
https://example.localhost
https://react.localhost
We'll assume that all of these websites are defined in a single main.ts
.
Option 1: Not using the folder name
We could just allow arbitrary folder names, and just use a CNAME at the root of the app, specifying the domain name.
assets.smallweb.run
It sounds like a fine solution, but it means that every smallweb website would need to include it. I really want single-file websites to be able to exist, and I feel like file based routing is a core feature of smallweb, so I did not go with this option.
Option 2: Using a Nested structure
/
├── localhost
│ ├── example
│ │ └── main.ts
│ └── react
│ └── main.ts
├── me
│ └── pomdtr
│ └── main.ts
└── run
└── smallweb
├── main.ts
├── assets
│ └── main.ts
└── readme
└── main.ts
Of course, this is not acceptable. If we look at the /run/smallweb
folder, we
can see that it contains both:
- the code of the
https://smallweb.run
homepage at his root. - the code of
readme
andassets
subdomains
If we used a git repository to manage each of those websites, this would quickly become a mess.
To counter this, we can add a convention: if the request target a root domain,
it will be automatically redirected to the www
domain.
/
├── localhost
│ ├── example
│ │ └── main.ts
│ └── react
│ └── main.ts
├── me
│ └── pomdtr
│ └── www
│ └── main.ts
└── run
└── smallweb
├── assets
│ └── main.ts
├── readme
│ └── main.ts
└── www
└── main.ts
This looks better! However, it still feels like we have some uncessary nesting.
For example, the /run
folder only has one subfolder: /run/smallweb
. Folders
are supposed to group related websites, but websites sharing the same TLD
probably have nothing in common.
Even worse, pomdtr.me
requires 3 (!!!) level of nesting: /me/pomdtr/www
.
Option 3: 2-level structure
Instead of splitting on .
, we'll use the apex domain as the first level of
subfolder, and the subdomain as the second one.
If a request target the apex domain, will automatically redirect it to the www
subdomain.
/
├── localhost
│ ├── example
│ │ └── main.ts
│ └── react
│ └── main.ts
├── pomdtr.me
│ └── www
│ └── main.ts
└── smallweb.run
├── www
│ └── main.ts
├── assets
│ └── main.ts
└── readme
└── main.ts
We still have some uncessary nesting (pomdtr/www
), but we get meaningful
groups in exchange.
Here the folder structure kind of reflect the process of updating DNS records in cloudflare.
Option 4: Flat structure
Let's drop the nesting, and use the domain name as the folder name:
/
├── assets.smallweb.run
│ └── main.ts
├── example.localhost
│ └── main.ts
├── pomdtr.me
│ └── main.ts
├── react.localhost
│ └── main.ts
├── readme.smallweb.run
│ └── main.ts
└── smallweb.run
└── main.ts
Using the domain name as the folder looks kind of ugly, but it avoid the nested folders problem entirely. One big advantage of this architecture is that you can create a new website from a git repository by just doing:
git clone <repo-url> <hostname>
My main gripe with it (outside of the noisy folder names), is that related
websites appears in different places in the file tree (ex: react.localhost
and
example.localhost
are not next to each others).
We can fix it by reversing the folder names:
/
├── localhost.example
│ └── main.ts
├── localhost.react
│ └── main.ts
├── me.pomdtr
│ └── main.ts
├── run.smallweb
│ └── main.ts
├── run.smallweb.assets
│ └── main.ts
└── run.smallweb.readme
└── main.ts
I quite like this compromise, but I'm not sure it would address the noisyness reported by the community.
What do you think ?
Here are the two options I'm considering as default:
- 2-level structure
- Reversed Flat structure
Writing this article, I've come to gain more appreciation of the two level-structure, as it mirrors the process of setting up DNS record in your domain registrar. However, the reversed flat structure is far more straightforward, which is a plus in my book.
I wonder if we should support both options (remix-style).
I would love to hear your thoughts on all of this. Make sure to join the discord channel if you want your voice to be heard.