Real names
Every service gets its own hostname, so cookies, CORS, OAuth redirect URLs and subdomain routing behave the way they do in production.
open source · macOS
https://api.myapp.lokal instead of http://localhost:4000, with a certificate your browser, curl, Node and Python trust. Set it up once. After that, adding a service takes milliseconds and never asks for a password.
brew install mehmetkose/tap/lokalThen run lokal setup once.
Star mehmetkose/lokal
$ lokal setup # once; asks for your password$ lokal project create myapp # claims myapp.lokal$ lokal service add api 4000✓ api.myapp.lokal → localhost:4000 (ready in 8ms) https://api.myapp.lokal$ curl https://api.myapp.lokal # works: no -k, no --cacertwhy lokal
Every service gets its own hostname, so cookies, CORS, OAuth redirect URLs and subdomain routing behave the way they do in production.
Secure cookies, HTTP/2 and mixed-content rules work like they do in production, without -k, --insecure or clicking through browser warnings.
Adding, changing or removing a service needs no sudo, no prompts and no restarts.
Every listener is on loopback, and names are never announced on your network.
lokal's CA can only sign .lokal and .local names, never public domains or IP addresses. Even a leaked key can't impersonate a real website.
WebSockets (Vite, Next.js and webpack hot reload), server-sent events, long polling, streaming uploads and HTTP/2 all pass through.
Every command is safe to re-run, every list command has a stable --json output, and exit codes say what went wrong.
Written in Rust and licensed under Apache-2.0. Read the code, the spec and how v0.1 was built.
how it works
Creates a CA that can only sign .lokal and .local names, trusts it in the System keychain, points .lokal lookups at lokal's DNS server and installs a background service that runs as you. Apart from uninstalling, it's the only time lokal asks for your password.
lokal setupA project owns one domain, and each service is a subdomain routed to a port on your Mac. No sudo, no prompts, no restarts. When the command returns, the URL already works.
lokal service add api 4000lokal answers *.lokal lookups with loopback, issues each name its own certificate in memory and routes by Host and SNI to localhost:<port>.
lokal open api$ lokal project create shop # https://shop.lokal; becomes the current project$ lokal service add @ 3000 # the apex itself: https://shop.lokal → localhost:3000$ lokal service add api 4000 # https://api.shop.lokal$ lokal service add v1.api 4001 # nested names work too$ lokal service add db-ui 8080 --host 127.0.0.1$ lokal list # every project, service and URL$ lokal open api # open it in your browser$ lokal service update api --port 4002$ lokal service remove apiworks with your stack
performance
13.68ms
service add, p95
target < 300ms
0.19ms
proxy overhead, p95
target < 2ms
17.62MB
memory with 30 services
target < 30MB
104.45ms
restart → serving every name
target < 500ms
service add p95 13.68ms (< 300ms) okfirst request after add: oklookup first 1.25ms (< 20ms) oklookup repeat 0.53ms (< 5ms) okproxy overhead p95 0.19ms (< 2ms) okresumed TLS p95 1.67ms (< 10ms) oklokal list p95 6.54ms (< 30ms) oklokal status p95 6.44ms (< 30ms) okRSS with 30 services 17.62MB (< 30MB) okidle CPU over 10s 0.00% (< 0.5%) okrestart → serving all names 104.45ms (< 500ms) okall names served after restart: okMeasured by lokal's live test on macOS 27.0 with lokal 0.1.0, against the real background service. TLS is verified exactly the way macOS verifies it, and headless Chrome loads the pages. 24 of 25 checks passed; the optional Safari check was skipped.
Targets & resultsfor coding agents
Every command is safe to re-run, so an agent can repeat a step without breaking anything.
lokal list --json and lokal status --json are a stable contract ("schema": 1).
lokal --help has a section written for agents, so they can learn the workflow from the binary alone.
Agents never run lokal setup or sudo. If lokal isn’t set up, they ask you.
lokal v0.1 was itself built by a coding agent working from the spec, with human sign-off for every privileged step. Read the build log.
Add this to your repo's AGENTS.md or CLAUDE.md:
## Local HTTPS URLs (lokal)
- This repo's dev servers run behind lokal: https://web.myapp.lokal → localhost:3000,
https://api.myapp.lokal → localhost:4000.
- Set up routes with `lokal project create myapp` and `lokal service add <sub> <port>` (safe to re-run).
- When a URL fails, run `lokal doctor`. Never run `lokal setup` or sudo; ask the user.faq
mkcert?mkcert creates locally trusted certificates. You still have to configure DNS (or /etc/hosts) and a server or proxy for each name. lokal does names, certificates and routing together, and its CA is name-constrained, so it can only sign .lokal and .local names.
Yes, for ports you publish on your Mac. With docker run -p 8080:80 …, run lokal service add app 8080. Containers can't resolve lokal's names themselves.
Not yet. lokal listens only on loopback by design. A LAN mode is on the roadmap.
/etc/hosts?No. lokal doctor warns if /etc/hosts has an entry that conflicts with a lokal name.
.lokal and .local, and not .test or .internal?The CA is trusted system-wide, so it is constrained to exactly the TLDs lokal serves. Adding .test or .internal would let a leaked key sign names that companies use for real internal hosts.
Once is sudo in Terminal. The second time is a macOS dialog, because macOS asks separately before it changes certificate trust settings.
For now, yes: macOS 14 Sonoma or later. It's developed and tested on Apple Silicon; Intel builds are published but haven't been tested yet. The code is structured so Linux and Windows support can be added later.
Yes. It's open source, licensed under Apache-2.0.
get started
Install with Homebrew, then run lokal setup once. Every service after that takes one command and no password.
brew install mehmetkose/tap/lokallokal setupRequires macOS 14 Sonoma or later. Or grab a prebuilt binary, or build from source.