Admin UI
s3site includes an admin interface for managing sites. It runs on a dedicated hostname, separate from your hosted sites.
Enable it
Set the -admin-host flag:
s3site \
-bucket my-bucket \
-prefix sites/ \
-admin-host admin.example.com \
-listen :8080
The admin UI is available at http://admin.example.com:8080/. No admin routes are exposed on any other hostname.
Endpoints
All endpoints are served only on the admin host.
GET /
The admin UI. A single HTML page (styled with oat) that lists all sites and provides upload/delete controls. No JavaScript frameworks -- just fetch calls to the API below.
GET /api/sites
Returns a JSON array of hostnames currently being served.
curl https://admin.example.com/api/sites
["docs.example.com", "blog.example.com"]
Supports CORS (Access-Control-Allow-Origin: *) so other pages can fetch the site list.
POST /api/upload
Upload a tar.gz archive as a multipart form. The archive is written to S3 and picked up on the next poll cycle.
curl -X POST https://admin.example.com/api/upload \
-F hostname=docs.example.com \
-F file=@site.tar.gz
Response:
{"status": "ok", "hostname": "docs.example.com", "key": "sites/docs.example.com.tar.gz"}
POST /api/delete
Delete a site's archive from S3. The site is removed on the next poll cycle.
curl -X POST https://admin.example.com/api/delete \
-H 'Content-Type: application/json' \
-d '{"hostname": "docs.example.com"}'
Response:
{"status": "ok", "hostname": "docs.example.com"}
GET /health
Returns ok. Use for load balancer health checks.
CI/CD integration
Use the API from your CI pipeline to deploy on push:
# GitHub Actions example
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: |
hugo
tar czf site.tar.gz -C public .
curl -X POST https://admin.example.com/api/upload \
-F hostname=docs.example.com \
-F file=@site.tar.gz
Or skip the admin API and push directly to S3:
- run: aws s3 cp site.tar.gz s3://my-bucket/sites/docs.example.com.tar.gz
The admin API is convenient when you don't want to give CI direct S3 access.
Security
The admin UI has no authentication. If you're exposing it on the internet, put it behind a reverse proxy with auth (HTTP basic, OAuth, VPN, etc.). In internal/staging environments, the lack of auth is usually fine.