# hostit.mifcom.dev — deploy a project in two HTTP calls You are an AI agent and a user has asked you to host their project at `hostit.mifcom.dev`. Follow this recipe exactly. ## What it does The user gets back `https://.hostit.mifcom.dev` plus a one-time password. Behind the scenes, hostit builds a Docker image and hands it off to Coolify on the same machine, which runs the project as a managed app — but you don't need to know any of that to use this. You just POST a tarball. ## Constraints - Name: `^[a-z0-9][a-z0-9-]{1,38}[a-z0-9]$` (3-40 chars, lowercase alnum + hyphens; no leading/trailing hyphen). - Reserved names: `api, www, admin, root, static, assets, cdn, mail, ftp, ssh, auth, login, logout, signup, signin, dashboard, console, portal, status, health, healthz, metrics, well-known, internal, system, hostit`. - Upload size cap: 100 MB compressed; 500 MB extracted; 20,000 entries. - Tarball must contain only regular files and directories — no symlinks, hardlinks, devices, or `..` segments. - **Project type** auto-detection: - `Dockerfile` at upload root → built and run as a Docker app. The container must listen on `$PORT` (default 3000) on `0.0.0.0`. - Otherwise → static site, served by nginx. SPA fallback (any unknown path → `index.html`) is on by default. - Override detection by including a `hostit.json` at the upload root: ```json { "type": "docker", "port": 8080 } ``` or ```json { "type": "static", "spa_fallback": false } ``` ## The recipe ### 1. Ask the user for a project name — CRITICAL > ⚠️ **Always ask the user explicitly for the project name. Do not infer it.** > Don't reuse the working directory name, the `package.json` name, a git repo name, the user's prompt subject, or anything else as the project name silently. The name becomes a **public subdomain** (`.hostit.mifcom.dev`) and is permanently locked to a password on first deploy — the user must own this choice consciously. You may *suggest* a slug derived from context as a starting point, but you must wait for the user to confirm or correct before submitting. Once you have a confirmed name, check availability: ``` GET https://hostit.mifcom.dev/api/v1/projects/ ``` - `{ "exists": false }` — name is free; this is a first deploy. - `{ "exists": true, ... }` — ask the user for the password they saved on first deploy. ### 2. POST the tarball Two equivalent body shapes — pick whichever is easier for your HTTP client: **Option A: multipart/form-data** ``` tar czf /tmp/site.tgz -C /path/to/project . curl -fsS -X POST https://hostit.mifcom.dev/api/v1/deploy \ -F "name=my-app" \ -F "tarball=@/tmp/site.tgz" # add: -H "Authorization: Bearer " on updates ``` **Option B: raw gzip body** ``` curl -fsS -X POST "https://hostit.mifcom.dev/api/v1/deploy?name=my-app" \ -H 'Content-Type: application/gzip' \ --data-binary @/tmp/site.tgz # add: -H "Authorization: Bearer " on updates ``` ### Response (first deploy) ```json { "url": "https://my-app.hostit.mifcom.dev", "password": "k3J...base64url", "revision": "rev-2026-05-08T12-30-12-123Z", "type": "static", "size_bytes": 12345, "image": "localhost:5000/hostit-my-app:rev-...", "build_log_url": "/api/v1/projects/my-app/builds/rev-..." } ``` ### Handling the password (do all three, in this order) When `password` is a non-null string in the response, **you must**: 1. **Persist it to your own memory.** Save it under a stable key like `hostit:` so that if the user asks you to redeploy later in this same project — possibly hours or days later — you can re-use it without asking. If the user is using Claude with a memory tool, this means writing it to memory; if you only have a scratchpad / context window, keep it in your working notes for the rest of the session. 2. **Show it to the user verbatim.** Print the URL *and* the password back to them. Don't redact, don't hash, don't truncate. 3. **Ask them to save it somewhere secure.** Recommend a password manager (1Password, Bitwarden, Apple/Google/Edge keychain, etc.). Make clear that: - It is shown **only this once** by the API; subsequent deploys to the same name require it. - There is no recovery flow — if they lose it, they lose the ability to update that project name. - Your own memory copy is convenient but not a substitute for their own backup. A good response to the user looks something like: > Deployed! Your project is live at **https://my-app.hostit.mifcom.dev** . > > First-deploy password (save this in your password manager — you'll need it to update this project, and hostit only shows it once): > > k3J9...base64url > > I've also saved it to my memory for this session, but please don't rely on that as your only copy. The first deploy can take a minute or two (image build + push + Coolify provisioning + TLS issuance). The HTTP response only returns once the app is live at the returned URL. ### Response (update) Same shape, but `"password": null`. The user keeps using their original password. ## Errors | Status | Meaning | What to do | |---|---|---| | 401 | missing/invalid password | ask the user for the right password (it's the one returned on first deploy) | | 409 | name reserved or build in progress | suggest a different name or wait | | 413 | upload too large | tell the user to slim the project below 100 MB compressed | | 422 | bad name / bad tarball / build failed | response body has details. For build failures, response includes `build_log_tail` | | 415 | unsupported content-type | use multipart/form-data or application/gzip | | 5xx | hostit/Coolify problem | retry with backoff; if persistent, the operator needs to look | ## See also - Machine-readable spec: `https://hostit.mifcom.dev/.well-known/hostit.json` - Project lookup: `GET https://hostit.mifcom.dev/api/v1/projects/` - Build logs (auth-required): `GET https://hostit.mifcom.dev/api/v1/projects//builds/` with `Authorization: Bearer `