From dd8878ebcf972189c1267e7a1dafbda4c6b0b635 Mon Sep 17 00:00:00 2001 From: Tyler Koenig Date: Sun, 17 May 2026 14:53:14 -0400 Subject: [PATCH] feat(serve): add TLS support with --tls-cert and --tls-key flags Adds make cert target for self-signed dev certs and development guide. --- .gitignore | 1 + Makefile | 10 +++++- cmd/serve.go | 30 +++++++++++++--- docs/development.md | 87 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 docs/development.md diff --git a/.gitignore b/.gitignore index 467e2d1..0e6785b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Binary nib tmp/ +certs/ # Database *.db diff --git a/Makefile b/Makefile index 46ae0ff..f4e1981 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ BINARY := nib MODULE := github.com/lerko/nib GOFLAGS := -trimpath -.PHONY: build dev watch test lint fmt vet clean run help +.PHONY: build dev watch test lint fmt vet clean run cert help ## —— Build —————————————————————————————————— @@ -41,6 +41,14 @@ fmt-check: ## Check formatting (fails if unformatted) ## —— Utility ———————————————————————————————— +cert: ## Generate self-signed dev TLS cert (certs/) + @mkdir -p certs + openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ + -nodes -keyout certs/dev.key -out certs/dev.crt -days 365 \ + -subj "/CN=localhost" + @echo " certs/dev.crt and certs/dev.key ready" + @echo " usage: make run ARGS=\"serve --tls-cert certs/dev.crt --tls-key certs/dev.key\"" + clean: ## Remove build artifacts rm -f $(BINARY) coverage.out diff --git a/cmd/serve.go b/cmd/serve.go index 8305433..157548a 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -20,6 +20,8 @@ var WebFS fs.FS var ( servePort int serveDev bool + tlsCert string + tlsKey string ) var serveCmd = &cobra.Command{ @@ -29,12 +31,19 @@ var serveCmd = &cobra.Command{ } func init() { - serveCmd.Flags().IntVar(&servePort, "port", 0, "port to listen on (default 4444)") + serveCmd.Flags().IntVar(&servePort, "port", 0, "port to listen on (default 4444, or 4443 with TLS)") serveCmd.Flags().BoolVar(&serveDev, "dev", false, "enable CORS for development") + serveCmd.Flags().StringVar(&tlsCert, "tls-cert", "", "path to TLS certificate file") + serveCmd.Flags().StringVar(&tlsKey, "tls-key", "", "path to TLS private key file") rootCmd.AddCommand(serveCmd) } func runServe(_ *cobra.Command, _ []string) error { + useTLS := tlsCert != "" && tlsKey != "" + if (tlsCert != "") != (tlsKey != "") { + return fmt.Errorf("both --tls-cert and --tls-key are required for TLS") + } + port := servePort if port == 0 { if envPort := os.Getenv("NIB_PORT"); envPort != "" { @@ -43,6 +52,8 @@ func runServe(_ *cobra.Command, _ []string) error { return fmt.Errorf("invalid NIB_PORT: %w", err) } port = p + } else if useTLS { + port = 4443 } else { port = 4444 } @@ -69,12 +80,23 @@ func runServe(_ *cobra.Command, _ []string) error { defer stop() go func() { - fmt.Printf("nib serving on %s\n", addr) + if useTLS { + fmt.Printf("nib serving on https://localhost%s\n", addr) + } else { + fmt.Printf("nib serving on http://localhost%s\n", addr) + } if serveDev { fmt.Println(" CORS enabled (dev mode)") } - if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { - fmt.Fprintf(os.Stderr, "server error: %v\n", err) + + var listenErr error + if useTLS { + listenErr = srv.ListenAndServeTLS(tlsCert, tlsKey) + } else { + listenErr = srv.ListenAndServe() + } + if listenErr != nil && listenErr != http.ErrServerClosed { + fmt.Fprintf(os.Stderr, "server error: %v\n", listenErr) } }() diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000..3005e4e --- /dev/null +++ b/docs/development.md @@ -0,0 +1,87 @@ +# Development Guide + +## Prerequisites + +- Go 1.24+ +- [air](https://github.com/air-verse/air) (live-reload, install: `go install github.com/air-verse/air@latest`) +- OpenSSL (for dev TLS certs) + +## Quick Start + +```sh +make cert # one-time: generate self-signed TLS cert +make watch # start dev server with live-reload (HTTP, port 4444) +``` + +## Make Targets + +### Build + +| Target | Description | +|--------|-------------| +| `make build` | Compile production binary (`./nib`) | +| `make dev` | Run dev server from source (HTTP, port 4444) | +| `make watch` | Live-reload dev server via air — auto-rebuilds on save | + +### Quality + +| Target | Description | +|--------|-------------| +| `make test` | Run all tests | +| `make test-v` | Run all tests with verbose output | +| `make test-cover` | Run tests with per-function coverage report | +| `make lint` | Run vet + format check | +| `make vet` | Static analysis via `go vet` | +| `make fmt` | Auto-format all Go files | +| `make fmt-check` | Check formatting without modifying (CI-friendly) | + +### Utility + +| Target | Description | +|--------|-------------| +| `make cert` | Generate self-signed dev TLS cert in `certs/` (valid 365 days) | +| `make clean` | Remove build artifacts | +| `make tidy` | Tidy and verify Go module dependencies | +| `make run ARGS="..."` | Build then run with custom args | +| `make help` | List all targets | + +## TLS + +nib supports TLS via `--tls-cert` and `--tls-key` flags on the `serve` command. + +### Dev (self-signed) + +```sh +make cert +make run ARGS="serve --tls-cert certs/dev.crt --tls-key certs/dev.key" +``` + +Serves HTTPS on port 4443. Browser will warn about the self-signed cert — accept once. + +This is needed for features that require a secure context (e.g. clipboard API). + +### Production + +For production, use a reverse proxy (Caddy, nginx) with real certificates in front of nib's HTTP server. Alternatively, pass real cert/key paths directly: + +```sh +./nib serve --tls-cert /path/to/cert.pem --tls-key /path/to/key.pem +``` + +### Port Defaults + +| Mode | Default Port | +|------|-------------| +| HTTP | 4444 | +| HTTPS | 4443 | + +Override with `--port` or the `NIB_PORT` environment variable. + +## Typical Workflow + +1. `make cert` — once, generates dev TLS cert +2. `make watch` — start coding, air rebuilds on save +3. Edit code, save, changes appear automatically +4. `make test` — verify nothing broke +5. `make lint` — check formatting and vet +6. Commit and push -- 2.52.0