# xanders.page :: the server roadmap

**This file is about servers and services.** How the site gets built is
`README.md`. What the site is pretending to be, and everything still to make,
is `THE-MACHINE.md`.

It used to open with a long section on the public site as well, written before
any of it existed: it called the project Nexus-7, put every file under a
`site/` directory that was never created, described the 88x31 badges as "drawn
in CSS so there is nothing to download" when there are now 6,876 real GIFs on
disk, and said "every link in the sidebar currently points at a page that does
not exist yet". All of it had been overtaken. Keeping a plan that describes a
different project is worse than having no plan, because you read it and
believe it.

```
  xanders.page          ::  the public creative hub (this repo)
  auth.xanders.page     ::  Authelia login page
  app.xanders.page/*    ::  private services, behind Authelia
  search.xanders.page   ::  SearXNG  (public, re-skinned)
  reddit.xanders.page   ::  Redlib   (public, re-skinned)
```

---

## Browser support: read this first

This is the constraint that shapes everything, so it goes at the top.

| Machine | OS | Highest Safari |
|---|---|---|
| 2010 MacBook Pro | Mavericks 10.9 | Safari 9.1.3 (ships with 7) |
| 2012 MacBook Pro | High Sierra 10.13 | Safari 13.1 (ships with 11) |

Mavericks is the real limit. Safari 7 is from 2013 and a lot of modern CSS
simply does not exist in it. This site is written to work there.

### Deliberately not used anywhere

| Feature | Needs | Used instead |
|---|---|---|
| `var(--name)` CSS variables | Safari 9.1 | hardcoded hex, documented at the top of the CSS |
| `display: grid` | Safari 10.1 | floats |
| flexbox `gap` | Safari 14.1 | margins |
| `inset: 0` shorthand | Safari 14.5 | `top/right/bottom/left` |
| `clamp()` | Safari 13.1 | fixed sizes |
| `String.padStart` | Safari 10 | a hand written `pad()` in main.js |
| `forEach` on a NodeList | Safari 10 | ordinary `for` loops |
| arrow functions, `let`, `const` | Safari 10 | `var` and `function` |
| `<marquee>` | removed from browsers | a CSS keyframe animation |

Gradients and transforms carry `-webkit-` prefixes for the same reason. This
is why you will see every gradient written out twice: the prefixed line first,
then the standard line. Safari 7 reads the first, everything modern reads the
second and ignores the first.

### The one safe upgrade

If you ever want real frosted glass behind the panels, add this to `.glass`:

```css
-webkit-backdrop-filter: blur(8px);
```

Browsers without it ignore the line entirely and you keep the plain gradient.
Nothing breaks. That pattern is called progressive enhancement and it is the
correct way to use new CSS when you have old machines to support.

### How to actually test

Do not trust a modern browser to tell you if Safari 7 is happy. Load the site
on the 2010 machine before you get attached to anything.

---

## The private wing

**This is infrastructure, not web design.** The rule that matters:

> Do not write your own login. Put a reverse proxy in front of everything and
> let dedicated software handle authentication.

Your own auth code is the highest risk thing you could write as a beginner,
and you would be putting your photos, email and password vault behind it.
This is not a skill issue. Experienced developers do not roll their own auth
either.

### The shape

```
internet -> Caddy (TLS, reverse proxy)
              |
              +-- forward_auth --> Authelia  (login + 2FA)
              |
              +-- /music   -> Navidrome
              +-- /photos  -> Immich
              +-- /files   -> Nextcloud
              +-- /mail    -> Roundcube
```

Caddy asks Authelia about every single request. No session, no access. The
individual services never have to be trustworthy, because nothing
unauthenticated ever reaches them.

### Build order

1. **Caddy plus TLS on a subdomain.** One service, no auth yet. Understand
   what a reverse proxy does before adding anything to it.
2. **Authelia in front of one service.** Navidrome is a good first pick. It is
   pleasant to use and not a catastrophe if you misconfigure it.
3. **Turn on 2FA (TOTP).** Do this before service number two, not after.
4. **Add the rest one at a time**, verifying auth on each before the next.
5. **Only then** build the Aero dashboard. It is a static page of links using
   the same `style.css`. It holds no secrets and enforces nothing, because the
   proxy does all the real work. That is exactly why it is safe to make it fun.

### Three specific warnings

**KeePass: do not put it on the web.** Keep the `.kdbx` syncing through
Nextcloud and open it in KeePassXC locally. A browser reachable vault is a far
bigger risk than the convenience is worth, and it is the one file that unlocks
everything else you own.

**WhatsApp, iMessage and SMS: park this.** It needs a Matrix homeserver plus
mautrix bridges, and iMessage additionally needs a Mac or a jailbroken device
online permanently. It is a bigger project than everything else on this page
combined. Do it last, or not at all.

**"Security section to manage the server": be careful what that means.** A
read only status page is genuinely useful and safe: disk usage, services up or
down, last login, certificate expiry, fail2ban counts.

A page that *runs commands* on your server is a remote code execution
endpoint. If it is ever wrong, it is wrong catastrophically. Manage the box
over SSH. Let the web page only ever look, never touch.

### Vultr

Do these three today, before anything else. They cover most of the risk.

1. SSH keys only. Set `PasswordAuthentication no` in `sshd_config`.
2. Vultr cloud firewall: allow 22, 80, 443 only. Default deny everything else.
3. Enable 2FA on the Vultr account itself.

Then fail2ban and unattended security upgrades. That is a solid baseline and
it is most of what a "security section" would otherwise be nagging you about.

---

## Re-skinning Redlib and SearXNG

Genuinely the best early win here. Both are open source and both are themed
with ordinary CSS. No JavaScript, no rebuild, no forking.

- **SearXNG** supports custom themes and injecting your own stylesheet.
- **Redlib** serves a single CSS file you can override at the proxy.

You can reuse the `.glass` recipe from `style.css` directly, so your search
engine looks like it belongs to your site. High payoff, low risk, and it is
pure CSS practice, which is exactly what you should be doing right now.
