Local domains, local HTTPS, zero config — for real projects.
dnser gives every project on your machine a real domain (mailbox.test),
trusted HTTPS, and a supervisor that starts, stops, and revives your apps.
One Go binary orchestrating Caddy, a local DNS server, and process-compose.
# macOS (Homebrew) — includes Caddy, mkcert and process-compose
brew install --cask sdk-e/tap/dnser
# Linux (deb/rpm from the releases page)
# https://github.com/SDK-E/dnser/releases/latest
# From source
go install github.com/SDK-E/dnser/cmd/dnser@latestUpgrading from v1? Run dnser migrate in each project directory.
mkdir demo && cd demo && echo '<h1>hi</h1>' > index.html
dnser init static # scaffold .dnser.yaml for the current project type
dnser link . # register: DNS + TLS + supervisor config, journalled & reversible
dnser up # start dnser itself (DNS on :3533 fallback chain, Caddy, supervisor)
dnser start demo # start the app; readiness-gated, so "ready" means ready
open https://demo.testEvery command speaks JSON when you do: dnser status -o json, dnser doctor -o json.
Exit codes are stable (0 ok, 2 usage, 4 needs setup with remediation printed, 10 doctor found issues).
| Command | What it does |
|---|---|
dnser link . |
Wire the current project into DNS/TLS/supervision (safe to re-run) |
dnser start / stop / restart <project> |
Lifecycle, gated by a TCP readiness probe |
dnser status [-o json] |
Projects, phases, ports, DNS state |
dnser logs <project> [-f] |
App logs via the supervisor |
dnser explain <project> |
Where every config value came from (flag > env > dotenv > manifest > default) |
dnser doctor |
Diagnose resolver drift, stale plans, upstream DNS; prints fixes |
dnser journal |
Every mutation dnser ever made; journal revert <id> undoes one |
dnser dashboard |
Local web UI (loopback + token) |
dnser elevate |
Opt-in system integration (/etc/resolver); never implicit |
dnser uninstall |
Purge everything, verified against the mutation journal |
Ready-to-link projects live in examples/ — clone, cd, link, up:
hello-static— plain HTML served by Caddy with local HTTPS (no process at all)node-api— Node API honoring$PORTpython-api— Python stdlib server withavailability: on_requestmailbox-style— realistic v1-style manifest afterdnser migrate: multi-backend routes, SMTP side-service, custom DNS records
dnser (one binary, CLI-on-demand — no idle daemon)
┌─────────────┬──────────────────┬───────────────────┐
│ ctrld/DNS │ Caddy │ process-compose │
│ *.test → │ TLS (mkcert CA) │ start/stop/wake, │
│ 127.0.0.1 │ reverse proxy │ readiness probes │
└─────────────┴───────────────────┴───────────────────┘
every change journalled → dnser journal / revert / uninstall
- Zero-config first run: bare
dnserguides you; nothing privileged withoutelevate. - Fallback mode: no
/etc/resolverwrites; projects resolve while your normal browsing stays untouched. Unhandled queries always forward upstream. - Privileged ports degrade gracefully: 53→5353→35353, 80→8080, 443→8443.
- Your processes never run as root.
go build ./... && go test ./... && golangci-lint runDocumentation lives in docs/: getting started, CLI reference,
manifest reference, architecture and troubleshooting. Brand assets drop
into docs/brand/.
BUSL-1.1 © SDK Enterprises · hicham@sdk.enterprises