Tabucom · internal temporary static hosting

Publish a front end. Get a URL.

Send one POST with HTML, Markdown, or a ZIP of a prebuilt static site. Set an optional ttl query parameter to choose the lifetime, or omit it to keep the default 30-day retention.

No sign-inHTML · Markdown · ZIP30-day default expiryImmutable deployments
Static builds only. This service does not run npm install, Node.js servers, SSR, APIs, databases, or uploaded source code. Build on your machine or in CI, then publish the resulting dist/ or build/ directory.

One request

Publish from any agent or shell

These commands use this page's current origin automatically. The explicit localhost forms remain valid for local development.

1

HTML

curl -sS -X POST \
  "https://tabucom.vps.mark-thebault.pro/api/v1/publish?ttl=72h" \
  -H "Content-Type: text/html; charset=utf-8" \
  --data-binary @index.html
2

Markdown

curl -sS -X POST \
  "https://tabucom.vps.mark-thebault.pro/api/v1/publish?ttl=72h" \
  -H "Content-Type: text/markdown; charset=utf-8" \
  --data-binary @report.md
3

Static build ZIP

curl -sS -X POST \
  "https://tabucom.vps.mark-thebault.pro/api/v1/publish?spa=1&ttl=72h" \
  -H "Content-Type: application/zip" \
  --data-binary @site.zip
Explicit localhost example
curl -sS -X POST "http://localhost:8080/api/v1/publish?ttl=72h" -H "Content-Type: text/html" --data-binary @index.html
Exact 201 response shape
{
  "id": "019f2a56-7f0d-73c3-89ef-8f0d8c499f26",
  "url": "https://publish.tools.company/p/019f2a56-7f0d-73c3-89ef-8f0d8c499f26/",
  "createdAt": "2026-06-19T14:30:00Z",
  "expiresAt": "2026-06-22T14:30:00Z",
  "files": 37,
  "bytes": 1842032,
  "spa": true
}

Every successful request creates a new immutable deployment. There is no update operation and the expiry cannot be extended after publication.

Agent instruction: publish the build output, then return the deployment URL and expiry timestamp to the user.

Complete project workflow

Build Node projects before publishing

Vite, React, Vue, Svelte and similar projects are supported when they produce static browser files.

1

Build

npm ci
npm run build

Find the generated output, commonly dist/ or build/.

2

Package its contents

(cd dist && zip -qr ../site.zip .)

ZIP the directory's contents, not the parent directory.

3

Publish

curl -sS -X POST \
  "https://tabucom.vps.mark-thebault.pro/api/v1/publish?spa=1&ttl=72h" \
  -H "Content-Type: application/zip" \
  --data-binary @site.zip

Use spa=1 only when client-side routing needs fallback.

Archive contract

index.html must be at the ZIP root

CSS, JavaScript, images, fonts, JSON, WASM, media, source maps, and nested asset directories are served as static files.

Accepted
site.zip
├── index.html
├── assets/
│   ├── app.js
│   └── app.css
└── images/logo.svg
Rejected
site.zip
└── dist/
    ├── index.html
    └── assets/…

Fix with (cd dist && zip -qr ../site.zip .).

Serving behavior

Path URLs by default, wildcard hosts when configured

The response's url is authoritative. Follow it rather than constructing a URL yourself.

Path mode

Deployments are served at /p/{id}/. Build apps with relative asset paths or the matching base path. Root-relative URLs such as /assets/app.js point at the service root and may fail.

Path-mode deployments share the same browser origin as this documentation page and the publish API. Do not treat path mode as an isolation boundary for untrusted HTML or JavaScript.

Wildcard-domain mode

When PREVIEW_DOMAIN and wildcard DNS/TLS are configured, the service can return https://{id}.preview.tools.company/. This gives each deployment a separate browser origin and supports root-relative assets.

Optional SPA fallback and TTL

Publish with ?spa=1 for React Router or another client router. Add &ttl=72h or another positive duration when the deployment should expire sooner or later than the default 30 days. Existing files are served normally; an unknown route returns the deployment's index.html. Without SPA mode, unknown paths return 404.

Operational contract

Fixed expiry and bounded uploads

Limits protect a no-sign-in internal service from accidental overload and hostile archives.

RuleDefault
LifetimeClient-selected via ttl; defaults to 30 days (720 hours)
Compressed request100 MB maximum
Expanded ZIP500 MB maximum
ZIP entries10,000 maximum, including directories
Publish rate60 requests/hour/network peer

ZIPs with absolute paths, traversal, NUL bytes, symlinks, device files, duplicate normalized paths, conflicting file and directory paths, excessive nesting, or expansion beyond the limit are rejected. Partial uploads never become visible.

Machine-readable failures

Errors use stable codes

Check the HTTP status and error.code; display error.message to the user.

Error response

{
  "error": {
    "code": "missing_index",
    "message": "site must contain index.html at its root"
  }
}

Codes

  • unsupported_media_type, empty_body, invalid_spa, invalid_ttl — fix the request shape.
  • upload_too_large / too_many_files — reduce the artifact.
  • invalid_archive / missing_index — fix ZIP structure.
  • rate_limited — retry after the response delay.
  • method_not_allowed, internal_error, service_unavailable — correct the caller or retry later.

HTTP API

Small enough to learn at a glance

Machine clients can use the OpenAPI 3.1 document, llms.txt, or agent discovery document.

POST/api/v1/publish

Publish HTML, Markdown, or ZIP. Optional spa=1 and ttl=<duration>. Returns 201 after atomic publication.

GET/healthz

Readiness endpoint for container and ingress health checks.