# mii-http
> Turn a `.http` specs file into a real HTTP server — backed by the shell commands you already have.
`mii-http` reads a small declarative file describing endpoints, parameters, body schemas and the shell command that should run for each route, and brings up a typed, validated, sandboxed HTTP server around it. It's the fastest way to put a clean HTTP face on top of CLI tools, scripts and one-off utilities — without writing a server.
```http
GET /status
Response-Type text/plain
Exec: echo ok
GET /greet
Response-Type text/plain
QUERY name: /[a-zA-Z0-9_]+/
Exec: echo Hello, [%name]
```
```sh
$ mii-http examples/sample.http
$ curl localhost:8080/named/v1/greet?name=world
Hello, world
```
---
## Index
- [Why mii-http](#why-mii-http)
- [Quick start](#quick-start)
- [The `.http` format](#the-http-format)
- [CLI](#cli)
- [Editor support](#editor-support)
- [Architecture](#architecture)
- [Security model](#security-model)
- [Contributing](#contributing)
- [Related](#related)
---
## Why mii-http
- **Declarative.** Endpoints, types, headers, query and body schemas live in a single readable file.
- **Typed.** Every input is validated against a real type (`int`, `uuid`, ranges, unions, regexes, typed JSON, forms, …) before your command ever runs.
- **Safe by default.** Request values are type-checked and shell-quoted before execution. The `--check` command flags risky patterns.
- **Tiny.** One binary. No runtime, no framework to learn — write a spec, point `mii-http` at it.
## Quick start
Install the tool using cargo:
```sh
cargo install --locked mii-http
```
or, alternatively...
Build the binary:
```sh
cargo build --release
```
Write a spec — `hello.http`:
```http
VERSION 1
BASE /api
GET /hello
Response-Type text/plain
QUERY name?: /[a-zA-Z0-9_]+/
Exec: echo Hello, [%name]
```
Validate it, then run it:
```sh
mii-http --check hello.http
mii-http --addr 127.0.0.1:8080 hello.http
```
Try it out:
```sh
$ curl 'localhost:8080/api/v1/hello?name=mii'
Hello, mii
```
A larger, multi-endpoint example lives in [examples/sample.http](examples/sample.http).
## The `.http` format
A spec file has two parts: a **setup** block with global options, followed by one or more **endpoint** blocks. Comments start with `#`.
```http
VERSION 1 # mounts everything under /v1
BASE /named # …prefixed with /named
AUTH Bearer [HEADER API_TOKEN] # bearer-token auth from a header
MAX_BODY_SIZE 1mb
TIMEOUT 30s
GET /users/:user_id:uuid
Response-Type text/plain
Exec: echo user [:user_id]
POST /submit-form
Response-Type text/plain
BODY form {
username: /[a-zA-Z0-9_]+/
age?: int(0..150)
}
Exec: echo username=[$.username] age=[$.age]
```
### Inputs
| Query param | `[%name]` / `"Hello, {%name}"` |
| Path param | `[:name]` / `"user {:name}"` |
| Header | `[^Name]` / `"header {^Name}"` |
| Body field | `[$.field]` / `"field {$.field}"` |
| Whole body | `$` (as stdin) |
| User var | `[@name]` / `"var {@name}"` |
- `[ … ]` is **shell-piece interpolation** — use it for any shell word or shell-word group that contains request data, required or optional. Missing optional values drop the whole group.
- `{ … }` is **string interpolation** and is valid only inside quoted strings. Use it when the request data is part of a larger string. Missing optionals become empty strings.
- `$ | …` pipes the request body to the command's stdin.
- Bare references like `%name` in `Exec` are literal shell text, not interpolation. `--check` warns when they match declared inputs; escape them as `\%name` if you really want the literal text.
### Types
`int`, `float`, `boolean`, `uuid`, `int(1..10)`, `float(0.0..1.0)`, unions like `red|green|blue`, regexes `/.../`, `string`, `json`, typed JSON schemas, `form`, `binary`. See [specs.md](specs.md) for the authoritative reference and the rules around which types may flow into argv vs. stdin only.
## CLI
```text
mii-http <path> run the server
mii-http --check <path> validate the specs and exit
mii-http --check --json <path> validate and print JSON diagnostics
mii-http --addr 0.0.0.0:8080 <path> bind to a specific address
```
`--dry-run` is the recommended way to develop a spec: every request prints the exact command line that *would* have been executed, with all interpolations resolved.
`--check --json` emits the same validation result as machine-readable diagnostics for editor integrations.
## Editor support
A VS Code extension lives in [editors/vscode/mii-http](editors/vscode/mii-http). It adds syntax highlighting, `mii-http --check --json` diagnostics and basic completions for directives, types and `Exec` references.
Build the binary and the extension before launching the extension host or packaging a VSIX:
```sh
cargo build
cd editors/vscode/mii-http
bun install
bun run compile
bun run package
```
The CI workflow publishes downloadable VSIX files through GitHub, not the VS Code Marketplace. When `editors/vscode/mii-http/package.json` gets a new version on `main`, `.github/workflows/vscode-extension.yml` creates a `vX.Y.Z-vscode` release tag and attaches `mii-http-X.Y.Z.vsix` to the GitHub Release. Every workflow run also uploads the VSIX as an Actions artifact.
## Architecture
```
┌────────────┐ parse ┌──────────────┐ check ┌──────────────┐
.http ──▶│ parse:: │─────────────▶│ spec AST │────────────▶│ diagnostics │
└────────────┘ └──────────────┘ └──────────────┘
│
▼
┌──────────────┐ axum ┌──────────────┐
│ server:: │────────────▶│ exec:: │
│ routes & │ │ validate + │
│ validation │ │ shell run │
└──────────────┘ └──────────────┘
```
Source layout:
- [src/parse/](src/parse/) — `chumsky`-based parser for the `.http` format.
- [src/spec.rs](src/spec.rs), [src/value.rs](src/value.rs) — typed AST and value model.
- [src/check.rs](src/check.rs), [src/diag.rs](src/diag.rs) — semantic checks and `ariadne` diagnostics.
- [src/server.rs](src/server.rs) — `axum` routing, request validation, header/body/query decoding.
- [src/exec.rs](src/exec.rs) — shell rendering, interpolation, temp-file materialization and process execution.
## Security model
- `Exec` runs through `/bin/sh`; shell syntax written in the spec is trusted shell syntax.
- Request values interpolated with `[ … ]` or quoted-string `{ … }` are shell-quoted before execution.
- Inputs are validated against their declared types before the command is invoked.
- `string` and free `json` types are restricted to **stdin only**, so unconstrained text cannot become argv.
- `binary` bodies are written to a temp file and the path is passed as argv (or streamed via stdin).
- `MAX_BODY_SIZE`, `MAX_QUERY_PARAM_SIZE`, `MAX_HEADER_SIZE` and `TIMEOUT` are enforced at the request boundary.
- `mii-http --check` highlights overly-permissive regexes (e.g. `/.*/`) and other risky patterns before they reach production.
## Contributing
Issues and PRs are welcome.
- Run `cargo test` before opening a PR — the suites under [tests/](tests/) cover the parser, checker, value model and end-to-end execution.
- Run `cargo clippy --all-targets` and `cargo fmt`.
- For new spec syntax, update both [specs.md](specs.md) and the parser tests; the spec file is the source of truth.
- Keep changes focused — small, reviewable PRs land faster.
## Related
- [`zmij`](https://crates.io/crates/zmij) — the structured-process helper used internally for safe command execution.
- [`axum`](https://github.com/tokio-rs/axum) — the HTTP framework powering the runtime.
- [`chumsky`](https://github.com/zesterer/chumsky) and [`ariadne`](https://github.com/zesterer/ariadne) — parser combinators and diagnostics.