What this is
A web-page hosting service. From any machine you can create "sites", push HTML/CSS/JS/asset files into them over HTTP, and control who can see them. Files are served directly by this host:
- Path URL (always on): https://beast-builder.marklo.de/h/<slug>/
- Optional subdomain: https://<slug>.beast-builder.marklo.de/ (enable per-site)
A site can be public (anyone with the link) or private (needs an access key — good for sharing with friends), and can be open or temporarily closed.
Human vs. machine
Your other VPS never "reads docs" — it just calls the API with a token. You (a person) read this guide in the browser, then drop the token + a script onto that machine.
| For | Where | Auth |
|---|---|---|
| Human (this guide, management) | https://beast-builder.marklo.de/hosting | Dashboard login (basic auth) |
| Machine (your other VPS) | https://beast-builder.marklo.de/hostapi/v1/… | Bearer token only — no dashboard password |
Connect your other VPS in 3 steps
1. In this browser, go to API Keys → Create API Key → copy the token from the popup (looks like bbh_…).
2. On your other VPS, set the base URL + token and verify:
export BASE="https://beast-builder.marklo.de/hostapi/v1"
export TOKEN="bbh_paste-your-token-here"
curl -s "$BASE/ping" -H "Authorization: Bearer $TOKEN"
# expect: {"ok":true,"service":"beast-builder-hosting",...}
3. Publish a local site directory (multi-page, with subfolders):
#!/usr/bin/env bash
set -euo pipefail
BASE="https://beast-builder.marklo.de/hostapi/v1"
TOKEN="bbh_your-token"
SLUG="mysite" # decides the URL: /h/mysite/
DIR="./public" # your built static-site folder
auth=(-H "Authorization: Bearer $TOKEN")
# create the site (ignore error if it already exists)
curl -s -X POST "$BASE/sites" "${auth[@]}" -H "Content-Type: application/json" \
-d "{\"slug\":\"$SLUG\",\"name\":\"My Site\"}" >/dev/null || true
# upload every file, preserving relative paths (about.html, blog/post1.html, assets/x.png …)
( cd "$DIR" && find . -type f | sed 's|^\./||' | while read -r f; do
curl -s -X PUT "$BASE/sites/$SLUG/files/$f" "${auth[@]}" --data-binary "@$f" >/dev/null
echo "uploaded $f"
done )
# make sure it is open
curl -s -X PATCH "$BASE/sites/$SLUG" "${auth[@]}" -H "Content-Type: application/json" \
-d '{"status":"active"}' >/dev/null
echo "Live: https://beast-builder.marklo.de/h/$SLUG/"
Then open https://beast-builder.marklo.de/h/mysite/.
Common operations (run from your other VPS)
# deploy a whole multi-page site in one shot
zip -r site.zip . && curl -s -X POST "$BASE/sites/$SLUG/deploy-zip?clean=true" "${auth[@]}" -F "file=@site.zip"
# temporarily close / re-open (visitors get 503 while closed)
curl -s -X PATCH "$BASE/sites/$SLUG" "${auth[@]}" -H 'Content-Type: application/json' -d '{"status":"closed"}'
curl -s -X PATCH "$BASE/sites/$SLUG" "${auth[@]}" -H 'Content-Type: application/json' -d '{"status":"active"}'
# make private (share with friends) — response contains share_url with ?k=…
curl -s -X PATCH "$BASE/sites/$SLUG" "${auth[@]}" -H 'Content-Type: application/json' -d '{"visibility":"private"}'
# enable the per-site subdomain URL
curl -s -X PATCH "$BASE/sites/$SLUG" "${auth[@]}" -H 'Content-Type: application/json' -d '{"subdomain_enabled":true}'
# delete one page / delete the whole site
curl -s -X DELETE "$BASE/sites/$SLUG/files/old.html" "${auth[@]}"
curl -s -X DELETE "$BASE/sites/$SLUG" "${auth[@]}"
Bearer token
Create a token on the API Keys page (shown once). Send it on every API request:
Authorization: Bearer <your-token>
Base API URL: https://beast-builder.marklo.de/hostapi/v1 — public (not behind the dashboard login); the token is the only credential. The management UI under /hosting stays behind the dashboard login.
Quick check:
curl -s https://beast-builder.marklo.de/hostapi/v1/ping \ -H "Authorization: Bearer $TOKEN"
Endpoints
| Method & path | Purpose |
|---|---|
| GET /hostapi/v1/ping | Verify token & connectivity |
| GET /hostapi/v1/sites | List all sites |
| POST /hostapi/v1/sites | Create a site |
| GET /hostapi/v1/sites/{slug} | Site details + file list |
| PATCH /hostapi/v1/sites/{slug} | Update name/visibility/status/subdomain/index/rotate key |
| DELETE /hostapi/v1/sites/{slug} | Delete site + all files |
| GET /hostapi/v1/sites/{slug}/files | List files |
| PUT /hostapi/v1/sites/{slug}/files/{path} | Upload/replace one file (raw body or multipart file) |
| DELETE /hostapi/v1/sites/{slug}/files/{path} | Delete one file |
| POST /hostapi/v1/sites/{slug}/pages | Upload an HTML page from JSON {path, html} |
| POST /hostapi/v1/sites/{slug}/deploy-zip | Deploy a whole multi-page site from a .zip (?clean=true to wipe first) |
Create a site
curl -s -X POST https://beast-builder.marklo.de/hostapi/v1/sites \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"slug":"mysite","name":"My Site","visibility":"public"}'
Response includes url_path and (for private sites) access_key + share_url.
Upload a single page or asset
# raw body — content-type comes from the path extension curl -s -X PUT https://beast-builder.marklo.de/hostapi/v1/sites/mysite/files/index.html \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: text/html" \ --data-binary @index.html # nested path / asset curl -s -X PUT https://beast-builder.marklo.de/hostapi/v1/sites/mysite/files/assets/logo.png \ -H "Authorization: Bearer $TOKEN" --data-binary @logo.png
Upload an HTML page from JSON
curl -s -X POST https://beast-builder.marklo.de/hostapi/v1/sites/mysite/pages \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"path":"about.html","html":"<h1>About</h1><a href=\"index.html\">home</a>"}'
Deploy a whole multi-page site (.zip)
cd my-site && zip -r ../site.zip . && cd .. curl -s -X POST "https://beast-builder.marklo.de/hostapi/v1/sites/mysite/deploy-zip?clean=true" \ -H "Authorization: Bearer $TOKEN" \ -F "file=@site.zip"
Open / close & access control
# temporarily close (visitors get 503)
curl -s -X PATCH https://beast-builder.marklo.de/hostapi/v1/sites/mysite \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"status":"closed"}'
# make private + get a shareable key
curl -s -X PATCH https://beast-builder.marklo.de/hostapi/v1/sites/mysite \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"visibility":"private"}'
# -> response.share_url looks like https://beast-builder.marklo.de/h/mysite/?k=XXXX (send this to friends)
# enable the subdomain URL
curl -s -X PATCH https://beast-builder.marklo.de/hostapi/v1/sites/mysite \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"subdomain_enabled":true}'
Multi-page sites & interlinks
Within a site, link between pages using relative paths so links work under /h/<slug>/:
<a href="about.html">About</a> <!-- good --> <a href="blog/post1.html">Post 1</a> <!-- good (nested) --> <img src="assets/logo.png"> <!-- good --> <a href="/about.html">About</a> <!-- avoid: root-absolute breaks under /h/<slug>/ -->
The site root serves index.html by default (configurable per site). If you enable the subdomain URL, both relative and root-absolute links work because each site sits at its own root.
Deploy script for your other VPS
#!/usr/bin/env bash
set -euo pipefail
BASE="https://beast-builder.marklo.de/hostapi/v1"
TOKEN="bbh_xxx_your_token"
SLUG="mysite"
auth=(-H "Authorization: Bearer $TOKEN")
# 1) create the site (ignore error if it already exists)
curl -s -X POST "$BASE/sites" "${auth[@]}" -H "Content-Type: application/json" \
-d "{\"slug\":\"$SLUG\",\"name\":\"My Site\"}" || true
# 2) push every file in ./public, preserving relative paths
cd public
find . -type f | sed 's|^\./||' | while read -r f; do
curl -s -X PUT "$BASE/sites/$SLUG/files/$f" "${auth[@]}" --data-binary "@$f" >/dev/null
echo "uploaded $f"
done
# 3) make sure it is open
curl -s -X PATCH "$BASE/sites/$SLUG" "${auth[@]}" -H "Content-Type: application/json" \
-d '{"status":"active"}' >/dev/null
echo "Live at https://beast-builder.marklo.de/h/$SLUG/"