Bunmori

Sites

One-shot publishing of static HTML bundles to plip.cc

Sites

The Sites module is a parallel app under /sites for publishing static page bundles. It’s designed for agents and small tools that generate HTML and need a quick share link — upload a zip, get back a public URL on plip.cc.

Each upload becomes an immutable Site with a short, unguessable slug. Bundles live on a separate registrable domain (plip.cc) so uploaded HTML/JS can never read cookies, hit APIs, or otherwise touch your bunmori.com admin app — full origin isolation.

What you get

  • One-shot publishing — POST a zip, get back https://plip.cc/{slug}/.
  • User-scoped publishing tokens — one token per agent, used to create many sites under your account.
  • Web upload — the admin UI at /sites/new does the same flow from a browser.
  • Anonymous public read — anyone with the URL can view the site. No account, no login. Pages are served with noindex so they don’t show up in search.
  • File-type allowlist — only standard page assets (HTML, CSS, JS, images, fonts, JSON, SVG, fonts, PDF, …) are accepted. Executables, archives, and unknown binaries are rejected before upload.
  • Strong limits — per-file 10 MB, per-site 50 MB and 200 files, per-user 100 sites and 2 GB.

Security model

  • Cross-site by design. plip.cc and bunmori.com are different registrable domains, so uploaded JS can’t read your admin cookies or make credentialed requests against the admin API.
  • No content scanning. Pre-scanning HTML/JS for malicious behaviour is unreliable and trivially evaded. Defence is architectural: origin isolation, the file-extension allowlist, per-user quotas, and the ability to revoke a token / delete a site in seconds.
  • noindex by default. Sites are meant for sharing by link, not for discovery via search engines. The serve route always sets X-Robots-Tag: noindex, nofollow.
  • No Domain cookies. Auth.js cookies are host-only, so they never reach plip.cc.

API reference

All requests use bearer-token auth. Tokens are created at /sites/tokens in the admin UI and start with the prefix bs_.

POST /api/public/sites

Upload a zip bundle. Returns the created site’s metadata and public URL.

curl -X POST https://bunmori.com/api/public/sites \
  -H "Authorization: Bearer bs_xxxxx" \
  -F "bundle=@./site.zip" \
  -F "title=My share" \
  -F "indexPath=index.html"

Response (201):

{
  "id": "...",
  "slug": "abc12345",
  "url": "https://plip.cc/abc12345/",
  "fileCount": 4,
  "totalBytes": 12345
}

Errors:

  • 400 — invalid zip, disallowed extension, path traversal, missing entry file, zip-bomb compression ratio exceeded.
  • 401 — missing or invalid token.
  • 413 — bundle exceeds per-site or per-user storage quota.
  • 429 — upload rate limit exceeded.

GET /api/public/sites

List all sites owned by the token’s user.

GET /api/public/sites/{slug}

Metadata for one site.

DELETE /api/public/sites/{slug}

Delete a site (and all its R2 files). The public URL returns 404 immediately.

Limits

LimitValue
Per file10 MB
Per site50 MB and 200 files
Per user100 sites and 2 GB total
Upload rate20 uploads / hour / user
Tokens10 publishing tokens / user
Zip compression ratio100× (rejected as zip bomb above this)

Allowed file types

html htm css js mjs map json svg png jpg jpeg gif webp avif ico woff woff2 ttf otf eot txt md pdf xml webmanifest

Anything else is rejected at upload. Need another extension? Open an issue.

On this page