Files and routing
A site is a set of files served at https://<name>.ginta.page/ (or its own domain). index.html is the home page.
index.html → /
about.html → /about (/about.html redirects there)
blog/index.html → /blog/
blog/first.html → /blog/first
404.html → any unknown address (status 404)
_redirects → redirects and rewrites (below)
assets/app-B8f2.js → /assets/app-B8f2.js
img/hero.avif → /img/hero.avif
.well-known/security.txt
Links to .html pages are shown without .html. Root-relative links (/assets/app.js) work: every site has its own
origin.
File types
| Text (editable with find/replace) | Binary (uploaded as bytes) |
|---|---|
html css js mjs json map webmanifest xml svg txt md csv, _redirects |
png jpg jpeg webp avif gif ico woff woff2 ttf otf pdf wasm mp4 webm mp3 |
Names: letters, digits, ., -, _; folders up to 10 deep; a leading _ is fine (_next/, _astro/); hidden files
aren't (except .well-known/). _g/, _m/, _ginta/, f/, s/ are Ginta's. A binary file must really be what its
extension says. How many files and MB: limits.
_redirects
Netlify / Cloudflare Pages format, one rule per line:
/old-menu /menu 301
/blog/:slug /news/:slug 301
/docs/* /help/:splat 302
/shop https://shop.example.com/ 302
/* /index.html 200
- Status 301 (default), 302, 303, 307, 308: redirects. They apply before files.
- Status 200: a rewrite (another file answers, the address stays). Only when no file matches, so
/* /index.html 200is the single-page-app fallback. - Redirects to another website work on paid plans.
Uploading
create_site/update_site:fileswithcontent(text) orcontent_base64(binary).upload_files: many files, or aurlper file Ginta downloads; no files → an upload link for a.zip(Claude Code).- Every change is a version;
restore_versionundoes one. The last 20 versions are kept.
JavaScript
Your own scripts, modules and WebAssembly run as they are. Not allowed: scripts from other websites (bundle libraries
instead), service workers, password or card-number fields (also when JavaScript creates them), forms posting to other
websites, pages imitating a bank/payment/tech login, crypto seed phrases. Pages may only fetch their own address:
outside APIs go through a connection.
Forms
<form method="POST" action="/f/__SITE_ID__">
<input type="text" name="_gotcha" hidden>
<input name="email" type="email" required> <textarea name="message"></textarea> <button>Send</button>
</form>
__SITE_ID__ is filled in at publish. With fetch(…, { headers: { Accept: "application/json" } }) the answer is JSON.
Messages: get_messages and Your sites → Messages.
REST API
The same with a signed-in session (cookie) and an Origin: https://ginta.app header:
POST /api/sites |
create { name, files } |
PUT /api/sites/<id> |
change { edits, files, delete, notify_email } |
GET /api/sites/<id>/files |
read |
POST /api/sites/<id>/upload-link |
{ "mode": "replace" | "merge" } → upload link for a .zip |
GET /api/sites/<id>/versions, POST /api/sites/<id>/rollback |
history, undo |
GET /api/sites/<id>/stats, GET /api/sites/<id>/speed |
visitors, scores |
PUT /api/sites/<id>/secrets/<NAME>, PUT /api/sites/<id>/connections/<name> |
secrets, connections |