# midas.mifcom.dev — deploy a project in two HTTP calls > **Tip for users:** the easiest way to use this is `https://midas.mifcom.dev/`. Sign in, click "Copy instructions for agent", paste the result into your AI assistant. The prompt already contains your personal API token; you don't need to read the rest of this file. The rest of this document is the contract your AI then follows. You are an AI agent and a MIFCOM user has asked you to host their project at `midas.mifcom.dev`. Follow this recipe exactly. ## What it does - One project = one subdomain: `https://.midas.mifcom.dev`. - A project can be **static files** (HTML/CSS/JS/assets) or a **Dockerfile-based app**. - You upload a single tar+gzip archive of the project directory. - Auto-detection: `Dockerfile` at upload root → docker app; otherwise static. - **Every project is behind Microsoft Entra SSO.** Anonymous visitors are redirected to login. - The owner chooses **visibility** at first deploy: `tenant` (everyone in the MIFCOM Entra tenant sees it AND it appears in the dashboard directory), `unlisted` (everyone signed into the MIFCOM tenant with the URL can view it, but it doesn't appear in the dashboard), or `private` (only the owner + people they explicitly share with via the dashboard). - The **owner is derived from your API token** — there is no `creator` field anymore. ## Step 0 — Get an API token To deploy anything you need a MIDAS personal API token. Most users get this via the welcome page: 1. The user opens `https://midas.mifcom.dev/` and signs in with their Microsoft account. 2. They click "Copy instructions for agent" and paste the result here. **That paste already contains the API token + this recipe.** If a user manually navigates to `/account` instead, they can regenerate their key there and paste it to you directly. There is one active API token per user. If you have one, save it in your own memory under a stable key like `midas:token` and reuse it for every deploy by this user, for every project. There is no legacy password fallback — without a token, deploying is impossible. ## 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, midas, oauth2`. - 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. - Dockerfile apps: listen on `$PORT` (default 3000) on `0.0.0.0`. Override via a `midas.json` at the upload root. - **Static apps** must have an HTML entry file at the **upload root** (not nested in `dist/`, `public/`, `build/`). If your project's build output lives in a subfolder, repack from there: `cd && tar czf ../app.tgz .`. MIDAS auto-picks the only `.html` file as the entry when there's exactly one; declare `midas.json` `index` when there are multiple — see [Static entry resolution](#static-entry-resolution) below. ## The two-call recipe ### 1. Ask the user for the 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** (`.midas.mifcom.dev`, gated by SSO) and ownership is permanently bound to whoever first deploys it — 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. Then check availability: ``` GET https://midas.mifcom.dev/api/v1/projects/ Authorization: Bearer ``` - `{ "exists": false }` — name is free; first deploy. - `{ "exists": true, "is_owner": true, ... }` — you can update it. Skip steps 2 & 3. - `{ "exists": true, "is_owner": false, ... }` — someone else owns this name. Pick a different one. ### 2. Prepare the metadata (first deploy only) **`description`** — required, 1–280 chars, **English preferred** even when the user is speaking another language to you (the dashboard is shared across the team). Do not interrogate the user — *draft it yourself*, then confirm. > Look at the project for obvious signals — `README.md`, `package.json#description`, the `` of `index.html`, the project directory name — and write a **1–2 sentence English description**. Then show your draft to the user: > > > "Here's what I'll use as the description: **\"\<your draft\>\"**. Use as-is, or change?" **`visibility`** — required: `tenant`, `unlisted`, or `private`. > Ask the user which visibility they want. Explain all three: > > > "Three choices for who can see this: > > - **`tenant`** — anyone signed into midas with a MIFCOM Microsoft account can visit it and star it. It shows up on the dashboard directory. > > - **`unlisted`** — anyone signed in with a MIFCOM account who has the URL can visit it. Not listed on the dashboard. Good for demos and work-in-progress you want to share by link without putting it in front of everyone. > > - **`private`** — only you can see it. You can share it with specific MIFCOM colleagues by email from the dashboard. > > > > You can change this later in the dashboard. Which one?" The `creator` field is no longer accepted by the API — ownership comes from your token. ### 3. POST the tarball Two equivalent body shapes: **Option A: multipart/form-data** ``` tar czf /tmp/site.tgz -C /path/to/project . curl -fsS -X POST https://midas.mifcom.dev/api/v1/deploy \ -H "Authorization: Bearer <api-token>" \ -F "name=my-app" \ -F "tarball=@/tmp/site.tgz" \ -F "description=A short English description." \ -F "visibility=tenant" # or 'unlisted' or 'private' ``` **Option B: raw gzip body** ``` curl -fsS -X POST "https://midas.mifcom.dev/api/v1/deploy?name=my-app&visibility=tenant&description=A%20short%20English%20description." \ -H "Authorization: Bearer <api-token>" \ -H 'Content-Type: application/gzip' \ --data-binary @/tmp/site.tgz ``` **Updates** (re-deploying an existing project name): omit `visibility` (it's immutable via deploy — owner mutates it from the dashboard). `description` is optional on updates — omit to keep prior value. ### Response (first deploy) ```json { "url": "https://my-app.midas.mifcom.dev", "revision": "rev-2026-05-14T09-30-12-123Z", "type": "static", "visibility": "tenant", "owner_email": "you@mifcom.de", "size_bytes": 12345, "image": "localhost:5000/midas-my-app:rev-...", "build_log_url": "/api/v1/projects/my-app/builds/rev-..." } ``` Tell the user the URL and remind them that the dashboard at `https://midas.mifcom.dev/dashboard` is where they manage visibility/sharing/stars. 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. ## Static entry resolution For `type=static` deploys (the default when no `Dockerfile` is at the root), midas serves the build context behind nginx with `index <file>;`. The entry filename resolves like this — **you should pre-empt the ambiguous case before uploading**: | Situation at the upload root | What midas does | What you should do as the agent | |---|---|---| | `index.html` (or `index.htm`) exists | uses it as the index, ignores any other HTML | nothing | | Exactly one HTML file (e.g. `Foo.html`) | auto-picks it as the index, no `midas.json` needed | nothing | | Multiple HTML files, no `index.html` | returns **422** with `details.fix.kind = "ambiguous-html-entry"`, the list of candidates, and the exact `midas.json` shape to write | inspect candidates, **ask the user** which file is the entry, write a `midas.json` at the tarball root: `{"type":"static","index":"<chosen>.html"}`, retar, retry | | No HTML files at the root | returns **422** with `details.fix.kind = "no-html-at-root"` | the user's build output probably lives in a subfolder (`dist/`, `public/`, `build/`). Repack from that folder: `cd dist && tar czf ../app.tgz .`. If they really meant an API-only app, add a Dockerfile and retry with `type=docker`. | The `details.fix.suggested_midas_json` in the 422 response contains a ready-to-use JSON object — when in doubt, show it to the user and confirm rather than guessing. `midas.json` schema (all fields optional, at the upload root): ```json { "type": "static" | "docker", "index": "Foo.html" | ["Foo.html", "fallback.html"], "spa_fallback": true, "port": 3000 } ``` ## Errors | Status | Meaning | What to do | |---|---|---| | 401 | missing/invalid/revoked API token | tell the user to regenerate their token at `/account` (or copy a fresh prompt from `/`) | | 403 | you're trying to update a project owned by someone else | only the project owner can update; ask the user if they meant a different project | | 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 / missing visibility / missing description / **ambiguous-html-entry** / **no-html-at-root** / build failed | check `details.fix.kind` for a machine-readable hint; the body's `details.fix.suggested_midas_json` (when present) is a ready-to-write JSON object | | 415 | unsupported content-type | use multipart/form-data or application/gzip | | 429 | rate limit exceeded for this token | `Retry-After` header tells you how many seconds to wait | | 5xx | midas/Coolify problem | retry with backoff; if persistent, the operator needs to look | ## See also - **Dashboard (humans):** `https://midas.mifcom.dev/dashboard` — see your projects, star, manage visibility and sharing. - **API key:** `https://midas.mifcom.dev/account` — view prefix + dates, regenerate, or revoke. One active key per user. - Machine-readable spec: `https://midas.mifcom.dev/.well-known/midas.json` - List visible projects (JSON): `GET https://midas.mifcom.dev/api/v1/projects` (Bearer-authenticated) - Star/unstar a project: `POST https://midas.mifcom.dev/api/v1/projects/<name>/vote` - Build logs: `GET https://midas.mifcom.dev/api/v1/projects/<name>/builds/<revision>` (Bearer-authenticated; owner or shared-with viewers only)