mii-http 0.3.0

Turn a .http specs file into a real HTTP server, backed by the shell commands you already have.
Documentation

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.

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]
$ mii-http examples/sample.http
$ curl localhost:8080/named/v1/greet?name=world
Hello, world

Index


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:

cargo install --locked mii-http

or, alternatively...

Build the binary:

cargo build --release

Write a spec — hello.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:

mii-http --check hello.http
mii-http --addr 127.0.0.1:8080 hello.http

Try it out:

$ curl 'localhost:8080/api/v1/hello?name=mii'
Hello, mii

A larger, multi-endpoint example lives in 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 #.

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

Source Reference in Exec
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 for the authoritative reference and the rules around which types may flow into argv vs. stdin only.

CLI

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
mii-http -q | --quiet <path>          suppress request/error logs
mii-http --dry-run <path>             log commands instead of running them

--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. 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:

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:

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/ 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 and the parser tests; the spec file is the source of truth.
  • Keep changes focused — small, reviewable PRs land faster.

Related

  • zmij — the structured-process helper used internally for safe command execution.
  • axum — the HTTP framework powering the runtime.
  • chumsky and ariadne — parser combinators and diagnostics.