An open API service indexing awesome lists of open source software.

https://github.com/paltaio/page

Self-hosted static site host with access rules, snapshots, and analytics.
https://github.com/paltaio/page

axum docker rust self-hosted sqlite web-hosting

Last synced: about 6 hours ago
JSON representation

Self-hosted static site host with access rules, snapshots, and analytics.

Awesome Lists containing this project

README

          

# Page

Self-hosted host for static sites with a built-in admin. Upload a folder or a zip and it is live at its own address, with optional password, PIN, email, or country access rules, snapshots, and per-site analytics. It runs as a single binary with the admin UI and database embedded.

## Deploy

Requires Docker.

1. Start it, pointing at the URL visitors will use:

```sh
PAGE_PUBLIC_ORIGIN=https://page.example.com docker compose up -d
```

You can also edit the environment in `compose.yml` and run `docker compose up -d`.

2. Read the one-time admin token from the logs:

```sh
docker compose logs page | grep "initial admin token"
```

3. Open `https://page.example.com/admin/#?admintoken=` and register a passkey. That becomes the first admin account, and the token is then spent.

All state (sites, uploads, settings, analytics) lives in the `pages-data` volume mounted at `/data`. Back that up to back up everything.

## Reverse proxy

Run Page behind a TLS-terminating proxy. Set `PAGE_PUBLIC_ORIGIN` to the public URL and `PAGE_TRUSTED_PROXIES` to the proxy IP(s) so the visitor IP is read from `X-Forwarded-For`. The trusted-proxy setting is required for correct visitor analytics and country access rules. Passkeys require `PAGE_PUBLIC_ORIGIN` to match the address visitors actually use.

Country access rules only evaluate forwarded addresses from trusted proxies. Set `PAGE_TRUSTED_PROXIES` to the exact egress IP(s) of the proxy; values are matched as exact IPs, not CIDR ranges. Configure the proxy to overwrite `X-Forwarded-For` with the client address rather than appending to it. A request that carries a `Forwarded` or `X-Forwarded-For` header from a peer outside the trusted list fails the country check. Treat country rules as best-effort filtering, not a hard security boundary.

## Sign-in

Staff sign in with passkeys. To also offer email sign-in codes, set the `PAGE_SMTP_*` variables; email sign-in is off when no SMTP host is configured.

## GeoIP (optional)

Country analytics and country access rules need a GeoIP database. Place a MaxMind GeoLite2 (City or Country) or a db-ip `.mmdb` file in the data directory, or point `PAGE_GEOIP_DB` at one.

## Configuration

| Variable | Default | Description |
| --- | --- | --- |
| `PAGE_PUBLIC_ORIGIN` | `http://localhost:3000` | Public URL visitors use. Set this in production. |
| `PAGE_BIND` | `0.0.0.0:3000` | Listen address. |
| `PAGE_DATA_DIR` | `/data` | Directory holding all state. |
| `PAGE_TRUSTED_PROXIES` | none | Comma-separated proxy IPs trusted for `X-Forwarded-For`. |
| `PAGE_GEOIP_DB` | auto | Path to a GeoIP `.mmdb`. Defaults to scanning the data directory. |
| `PAGE_UPLOAD_MAX_BYTES` | `536870912` | Maximum upload size in bytes. |
| `PAGE_ADMIN_TOKEN_ENABLED` | `true` | Enables the one-time first-run admin token. |
| `PAGE_SMTP_HOST` | none | SMTP server for email sign-in codes. |
| `PAGE_SMTP_PORT` | `587` | SMTP port. |
| `PAGE_SMTP_TLS` | `true` | Use TLS for SMTP. |
| `PAGE_SMTP_FROM` | none | From address. Required when SMTP is set. |
| `PAGE_SMTP_USERNAME` | none | SMTP username. |
| `PAGE_SMTP_PASSWORD` | none | SMTP password. |

## Local development

Requires Rust and pnpm. The admin bundle is embedded at build time, so build it before the server:

```sh
pnpm --dir admin install
pnpm --dir admin build
cargo run
```

The server then listens on `http://localhost:3000`.