web-pylon: a personal website backend
Not quite a static site generator, not quite a full CMS: web-pylon is a minimalistic website backend
that lets you quickly get a personal website up and running with minimal fuss.
Features
- Read-only: web-pylon is designed to never write anything to a file, other than system logs.
You can trust that web-pylon will never write over your data, and that if any exploits are found
in web-pylon's source code or dependencies, they won't be able to alter any system files.
- Static database: One of the biggest draws of static site generators is that there is no
complicated database setup. The tradeoff for this is that metadata, such as page titles, must be
embedded in the pages themselves. Instead of either of these approaches, web-pylon uses a static
database: a JSON file that describes everything about how your site is laid out. This enables
web-pylon to have access to individual properties about specific pages without loading the entire
page into memory, but also means you don't need to fiddle with setting up MySQL just to run a
simple website.
- Fully customizable: web-pylon asks only that you provide a
page
template, a list
template,
an _index.md
page, and a static
directory; the rest is up to you. Sample templates and
stylesheets are distributed with web-pylon, but you are free to change them as much as you like to
get your site looking just right.
- Markdown and HTML: Author most of your content in Markdown, but drop to HTML in the page
itself, or even customize the Tera and HTML template files
if you need more power.
- Written in Rust: Common attacks on languages that are distributed in source form won't work on
web-pylon, because it's a compiled binary. This also means web-pylon is generally more performant
than systems reliant on a scripting language like Python or PHP.
Building
You must have a recent version of the Rust toolchain installed.
Simply run a build in release mode:
$ cargo build --release
The web-pylon
binary will be in the target/release
directory.
Installing
You will need a Linux server with some kind of reverse-proxy web server installed. I recommend
nginx.
- Create a folder
/opt/web-pylon
- Copy the
web-pylon
binary to /opt/web-pylon/bin
- Copy the
server/lib
directory to /opt/web-pylon/lib
- Copy the
server/web-pylon.example.toml
file to /opt/web-pylon/web-pylon.toml
.
- Create a directory readable by your web server that will hold your website content. Copy the
contents of the
sample
folder (not the sample
folder itself) to this directory.
- Edit
/opt/web-pylon/web-pylon.toml
to configure web-pylon
for your machine. At the absolute
minimum, you must set the content.root
property to the directory you created in the previous
step.
- Change the ownership of
/opt/web-pylon
and everything inside it, and also your content root, to
be owned by your web server user (e.g. www
or www-data
, depending on your distro).
- Create a link from
/etc/systemd/system/web-pylon.service
to
/opt/web-pylon/lib/systemd/system/web-pylon.service
.
- Start the
web-pylon
service.
Customizing
After step 5 in the previous section, your content root will have the following structure:
metadata.json
content/
pages/
_index.md
about.md
cv.md
posts/
sample-post.md
static/
stylesheets/
theme.css
templates/
_base.tera.html
atom_feed.tera.xml
list.tera.html
page.tera.html
post.tera.html
You are free to edit any of these files; the only hard requirements are that you have a
metadata.json
, content/_index.md
, and the files in the templates
directory.
Authoring
web-pylon uses a combination of the metadata.json
file (the static database) and the Markdown
files in the content
folder to build the structure of your website.
Pages
Pages are for static content that isn't really part of an overarching structure, besides the site
itself. Three example pages are included with the sample content: a home page (_index.md
), an
"About" page, and a CV page.
To create a new page, first add an entry in the pages
array in metadata.json
:
{
...
"pages": [
...
{
"name": "My New Page",
"slug": "my-new-page"
}
]
}
You can optionally add this page to the menu
array, if you would like it to be accessible in the
site.menu
template variable.
Then, simply create a Markdown file in the content/pages
directory named my-new-page.md
:
# My New Page
Wow! Markdown!
- Here's
- A
- List
[Links work too](https://example.com/)
[Here's a link back to the home page](/)
[And here's a link to this page](/page/my-new-page)
Then restart the web-pylon service to reload the static database:
$ sudo systemctl restart web-pylon
Posts
Posts are meant to be serialized, like a social media timeline. They don't have to be short or
barebones, but they are designed to form an ordered history; think of it kind of like an old-school
blog.
Posts are composed exactly the same way as pages, except the metadata goes in the posts
array of
the static database, and the content file goes in the content/posts
directory.
Posts require a little bit more metadata than pages:
- A publish date and an update date. These have to be specified in RFC
3339 format, which looks like this:
YYYY-MM-DDTHH:MM:SS+ZH:ZM
(or YYYY-MM-DDTHH:MM:SSZ
for UTC).
- A syndication ID. This is used to generate Atom feeds of your posts so that your visitors can
subscribe to you in an RSS reader. You can use any globally-unique ID for this value, but it is
recommended to use a "tag URL", which is a string in the following format:
tag:yourdomain.name,YYYY-MM-DD:/posts/your-post-slug
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# web-pylon: a personal website backend
Not quite a static site generator, not quite a full CMS: web-pylon is a minimalistic website backend
that lets you quickly get a personal website up and running with minimal fuss.
## Features
- **Read-only:** web-pylon is designed to never write anything to a file, other than system logs.
You can trust that web-pylon will never write over your data, and that if any exploits are found
in web-pylon's source code or dependencies, they won't be able to alter any system files.
- **Static database:** One of the biggest draws of static site generators is that there is no
complicated database setup. The tradeoff for this is that metadata, such as page titles, must be
embedded in the pages themselves. Instead of either of these approaches, web-pylon uses a static
database: a JSON file that describes everything about how your site is laid out. This enables
web-pylon to have access to individual properties about specific pages without loading the entire
page into memory, but also means you don't need to fiddle with setting up MySQL just to run a
simple website.
- **Fully customizable:** web-pylon asks only that you provide a `page` template, a `list` template,
an `_index.md` page, and a `static` directory; the rest is up to you. Sample templates and
stylesheets are distributed with web-pylon, but you are free to change them as much as you like to
get your site looking just right.
- **Markdown and HTML:** Author most of your content in Markdown, but drop to HTML in the page
itself, or even customize the [Tera](https://keats.github.io/tera/docs/) and HTML template files
if you need more power.
- **Written in Rust:** Common attacks on languages that are distributed in source form won't work on
web-pylon, because it's a compiled binary. This also means web-pylon is generally more performant
than systems reliant on a scripting language like Python or PHP.
## Building
You must have a recent version of the Rust toolchain installed.
Simply run a build in release mode:
```
$ cargo build --release
```
The `web-pylon` binary will be in the `target/release` directory.
## Installing
You will need a Linux server with some kind of reverse-proxy web server installed. I recommend
[nginx](https://nginx.org/en/).
1. Create a folder `/opt/web-pylon`
2. Copy the `web-pylon` binary to `/opt/web-pylon/bin`
3. Copy the `server/lib` directory to `/opt/web-pylon/lib`
4. Copy the `server/web-pylon.example.toml` file to `/opt/web-pylon/web-pylon.toml`.
5. Create a directory readable by your web server that will hold your website content. Copy the
contents of the `sample` folder (not the `sample` folder itself) to this directory.
6. Edit `/opt/web-pylon/web-pylon.toml` to configure `web-pylon` for your machine. At the absolute
minimum, you must set the `content.root` property to the directory you created in the previous
step.
7. Change the ownership of `/opt/web-pylon` and everything inside it, and also your content root, to
be owned by your web server user (e.g. `www` or `www-data`, depending on your distro).
8. Create a link from `/etc/systemd/system/web-pylon.service` to
`/opt/web-pylon/lib/systemd/system/web-pylon.service`.
9. Start the `web-pylon` service.
## Customizing
After step 5 in the previous section, your content root will have the following structure:
```
metadata.json
content/
pages/
_index.md
about.md
cv.md
posts/
sample-post.md
static/
stylesheets/
theme.css
templates/
_base.tera.html
atom_feed.tera.xml
list.tera.html
page.tera.html
post.tera.html
```
You are free to edit any of these files; the only hard requirements are that you have a
`metadata.json`, `content/_index.md`, and the files in the `templates` directory.
## Authoring
web-pylon uses a combination of the `metadata.json` file (the static database) and the Markdown
files in the `content` folder to build the structure of your website.
### Pages
Pages are for static content that isn't really part of an overarching structure, besides the site
itself. Three example pages are included with the sample content: a home page (`_index.md`), an
"About" page, and a CV page.
To create a new page, first add an entry in the `pages` array in `metadata.json`:
```
{
...
"pages": [
...
{
"name": "My New Page",
"slug": "my-new-page"
}
]
}
```
You can optionally add this page to the `menu` array, if you would like it to be accessible in the
`site.menu` template variable.
Then, simply create a Markdown file in the `content/pages` directory named `my-new-page.md`:
```
# My New Page
Wow! Markdown!
- Here's
- A
- List
[Links work too](https://example.com/)
[Here's a link back to the home page](/)
[And here's a link to this page](/page/my-new-page)
```
Then restart the web-pylon service to reload the static database:
```
$ sudo systemctl restart web-pylon
```
### Posts
Posts are meant to be serialized, like a social media timeline. They don't have to be short or
barebones, but they are designed to form an ordered history; think of it kind of like an old-school
blog.
Posts are composed exactly the same way as pages, except the metadata goes in the `posts` array of
the static database, and the content file goes in the `content/posts` directory.
Posts require a little bit more metadata than pages:
- A publish date and an update date. These have to be specified in [RFC
3339](https://www.rfc-editor.org/rfc/rfc3339) format, which looks like this:
`YYYY-MM-DDTHH:MM:SS+ZH:ZM` (or `YYYY-MM-DDTHH:MM:SSZ` for UTC).
- A syndication ID. This is used to generate Atom feeds of your posts so that your visitors can
subscribe to you in an RSS reader. You can use any globally-unique ID for this value, but it is
recommended to use a "tag URL", which is a string in the following format:
`tag:yourdomain.name,YYYY-MM-DD:/posts/your-post-slug`