Server Guide

The hostedat server is a single binary that handles everything: serving static sites, managing users, handling HTTPS, and exposing the API. No reverse proxy, no Docker, no external dependencies.

Quick start

Download the server binary for your platform from the downloads page, create a config file, and run it.

Choose your engine

The server ships with two JavaScript engine options for the worker runtime:

  • V8 (recommended) — JIT-compiled JavaScript for best worker performance. Available for Linux and macOS.
  • QuickJS — Interpreted JavaScript, runs on all platforms including Windows. No native dependencies required.

Both engines provide the same Workers API and features. If you don't use workers, either engine works identically for static site hosting.

# V8 (recommended) — amd64
curl -fsSL https://docs.hostedat.ditto.moe/downloads/hostedat-server-v8-linux-amd64 -o hostedat-server

# V8 (recommended) — arm64
curl -fsSL https://docs.hostedat.ditto.moe/downloads/hostedat-server-v8-linux-arm64 -o hostedat-server

# QuickJS — amd64
curl -fsSL https://docs.hostedat.ditto.moe/downloads/hostedat-server-linux-amd64 -o hostedat-server

# QuickJS — arm64
curl -fsSL https://docs.hostedat.ditto.moe/downloads/hostedat-server-linux-arm64 -o hostedat-server

chmod +x hostedat-server
# V8 (recommended) — Universal binary (Apple Silicon + Intel)
curl -fsSL https://docs.hostedat.ditto.moe/downloads/hostedat-server-v8-darwin-universal -o hostedat-server

# QuickJS — Universal binary (Apple Silicon + Intel)
curl -fsSL https://docs.hostedat.ditto.moe/downloads/hostedat-server-darwin-universal -o hostedat-server

chmod +x hostedat-server
# QuickJS only (V8 is not available on Windows)

# amd64
Invoke-WebRequest -Uri https://docs.hostedat.ditto.moe/downloads/hostedat-server-windows-amd64.exe -OutFile hostedat-server.exe

# arm64
Invoke-WebRequest -Uri https://docs.hostedat.ditto.moe/downloads/hostedat-server-windows-arm64.exe -OutFile hostedat-server.exe

Then create a config and start the server:

cp config.example.yaml config.yaml
# Edit config.yaml with your settings

./hostedat-server

First-run setup

On first launch with an empty database, the first user to register becomes the superadmin. This account has full control and cannot be demoted.

  1. Start the server
  2. Open the web UI at your configured domain
  3. Register the first account — this is your superadmin
  4. Configure registration settings (open, invite-only, or closed)

HTTPS & certificates

hostedat uses CertMagic (the ACME library from Caddy) to automatically obtain and renew wildcard TLS certificates from Let's Encrypt. This requires DNS-01 validation via Cloudflare.

config.yaml
# Listen on :443 for production HTTPS
listen: ":443"

# Cloudflare API token with Zone:DNS:Edit permission
cloudflare:
  api_token: "your-cloudflare-api-token"

The server will automatically obtain a wildcard cert for *.yourdomain.com and renew it before expiration. No cron jobs, no manual renewal.

Development mode (no TLS)

For local development, listen on a non-443 port and leave the Cloudflare token empty:

config.yaml
listen: ":8080"
cloudflare:
  api_token: ""

Database

hostedat uses GORM with SQLite by default. The database file is created automatically. For production, you can switch to PostgreSQL or MySQL by changing the driver and DSN:

config.yaml
# SQLite (default)
database:
  driver: sqlite
  dsn: ./data/hostedat.db

# PostgreSQL
database:
  driver: postgres
  dsn: "host=localhost port=5432 user=hostedat dbname=hostedat sslmode=disable"

# MySQL
database:
  driver: mysql
  dsn: "hostedat:password@tcp(localhost:3306)/hostedat?parseTime=true"

Static site features

hostedat supports several features that make it compatible with sites built for Netlify or Cloudflare Pages.

_redirects file

Place a _redirects file in the root of your uploaded site to define redirect and rewrite rules:

_redirects
# Redirects (browser URL changes)
/old-path    /new-path    301
/blog/*      /new-blog/:splat 302

# Rewrites (transparent, URL stays the same)
/api/*       https://api.example.com/:splat 200

# SPA fallback (must be last)
/*           /index.html  200
  • 301 / 302 — redirect (browser URL changes)
  • 200 — rewrite (serve target content at original URL)
  • * wildcard is captured and available as :splat
  • First match wins, evaluated top-to-bottom
  • Static files always take precedence over rules

_headers file

Custom response headers for matching paths:

_headers
/*
  X-Frame-Options: DENY
  X-Content-Type-Options: nosniff

/*.js
  Cache-Control: public, max-age=31536000, immutable

Custom 404

Include a 404.html file in your upload to show a custom 404 page when no file matches.

SPA mode

Toggle SPA mode per-site via the dashboard or API. When enabled, all unmatched routes serve index.html — equivalent to having /* /index.html 200 as the last _redirects rule.

Worker runtime

hostedat includes a Cloudflare Workers-compatible server-side JavaScript runtime. Include a _worker.js file in your deployment to intercept HTTP requests with JavaScript instead of serving static files directly. This enables dynamic content, API endpoints, authentication, server-side rendering, and more.

Workers have access to environment variables, KV storage, cron triggers, and the ASSETS binding to serve static files when needed. See the full Worker Runtime documentation for API reference, examples, and lifecycle details.

User roles

RoleCapabilities
superadmin Full control. First registered user. Cannot be demoted.
admin Manage users, sites, invites. Can be promoted/demoted by superadmin.
user Create sites, deploy, manage own API keys.

Running as a service

For production, run hostedat as a systemd service:

/etc/systemd/system/hostedat.service
[Unit]
Description=hostedat static site hosting
After=network.target

[Service]
Type=simple
User=hostedat
WorkingDirectory=/opt/hostedat
ExecStart=/opt/hostedat/hostedat-server
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
sudo systemctl enable hostedat
sudo systemctl start hostedat

Next: Configuration Reference for all config options.