The httpd subpackage provides a high-level, "batteries-included" HTTP server implementation that implements the webtyp.com/router contract.
The development mode's internalStrategy (in-memory server) consumes the httpd.NewRouter adapter to provide consistent routing behavior between development and production.
A project does not contain an HTTP main(). maingen.go writes one to
.build/server/main.go — inside the project, because outside the module tree
the Go toolchain cannot resolve the project's own imports — and the External
strategy compiles and runs it.
The generated main imports the project's routes package and calls
routes.Register on the server it builds, so development and production serve
the same route set from the same source. The previous
templates/server_basic.md scaffold is gone: it produced a committed
web/server.go whose routes had to be kept in sync by hand with the ones the
dev process registered, and they drifted.
Which strategy runs is decided by one file: a project with routes/routes.go
gets the compiled server; a project without one gets the in-memory asset server
and compiles nothing. A web/server.go that a developer writes themselves still
wins — the tool never overrides code the user owns — but nothing generates it.
adapter.go: Implementation ofrouter.Router,router.Context, etc., mapping them tonet/http.middleware.go: Built-inGzipandNoCachemiddlewares.static.go: Static file serving fromPublicDir.enforce.go: RBAC enforcement based onRequiresmetadata.tls.go/devcert.go/ca.go: AutoCert (Let's Encrypt), custom Cert/Key, andDevTLS. See the TLS section below.spki.go:DevCertSPKI(), the pin the development browser trusts.routes_endpoint.go: Optional JSON endpoint at/_routeslisting all registered routes.httpd.go: CoreServerorchestrator.
- Pattern Matching & Extraction: Delegated to
net/http.ServeMux. Routes registered as"GET /api/items/{id}"natively match parameters in Go 1.22+, accessible viactx.Param(name). - Pattern Validation: Delegated to
webtyp/routerviarouter.ValidatePattern. This server rejects patterns with wildcards (e.g.,{name...}) at registration time so that patterns unsupported by edge runtimes fail early on the dev server. - Introspection Endpoint: Delegated to
router.MountIntrospectionatrouter.IntrospectionPath(/_routes). This repo supplies theConfig.RoutesEndpointboolean toggle and theConfig.Policydescriber.
- Simple Entry Point:
httpd.New(config).Mount(modules).ListenAndServe()is the only way to start the server. - Standard Library Based: Uses
net/httpinternally but doesn't expose it in the public API. - Fails Fast: Validates configuration (like TLS modes or RBAC requirements) at startup rather than at runtime.
The server follows a secure-by-default model where routes are private unless explicitly marked as Public(). However, for a smooth development experience:
- Static Assets: Files served from
PublicDir(e.g., WASM binaries, JS, CSS) are always public. They bypass the RBAC middleware. - Root Path (
/) in Development: The internal strategy registers a default public route for/that servesPublicDir/index.htmlor a diagnostic message. This ensures that the frontend application is accessible immediately without manual RBAC configuration.
DevTLS is on by default in development. Serving HTTP locally and HTTPS in
production hides a class of bugs that only appear after deployment — Secure
cookies, SameSite=None, HSTS, mixed content — and makes it impossible to test
a PWA from a phone on the LAN.
It generates a two-level chain, the same shape mkcert and Caddy's internal
PKI use:
- The CA (
ca.go) —IsCA: true,KeyUsageCertSign | KeyUsageCRLSign,MaxPathLen: 0, no subject alternative names, long-lived. This is the file a device installs to trust the server. - The leaf (
devcert.go) — signed by the CA,IsCA: false,serverAuth, and a SAN set coveringlocalhost,127.0.0.1,::1and every non-loopback IPv4 of the host's interfaces. It is regenerated when that address set changes, so a laptop moving between networks does not keep presenting a certificate for an address it no longer has.
A single self-signed certificate cannot do this job: a leaf has CA:FALSE, and
iOS and Android refuse to use it as a trust anchor — the device shows no error,
the page simply keeps failing after the user has followed every instruction.
| Client | Mechanism | Trusts |
|---|---|---|
| The development browser | --ignore-certificate-errors-spki-list with DevCertSPKI() |
exactly the leaf's public key |
| A phone on the LAN | installs the CA from CAPath (/__webtyp/ca) |
anything the CA signs |
DevCertSPKI() returns the leaf's SPKI hash, never the CA's. Chrome matches
the flag against any certificate in the presented chain, so pinning the CA would
appear to work — and would silently grant the browser trust in every certificate
that CA ever signs, surviving a leaf rotation unnoticed.
Nothing is installed into the developer's own OS trust store. The browser is
launched by webtyp.com/devbrowser, so it is configured rather than convinced,
which needs no elevated privileges and leaves nothing behind.
On iOS, installing a profile is two steps and the platform announces only the first: install it (Settings → Profile Downloaded), then enable it under Settings → General → About → Certificate Trust Settings. A profile installed but not trusted behaves exactly like no profile at all.