Skip to content

Strip Local Development

Strip Local Development

Strip is a Go SSR app: Templ → Tailwind → Go binary. The Templ and Tailwind CLIs are not Go-managed dependencies; install them once before building.

Prerequisites

ToolHow to installNotes
Gomatch apps/strip/go.mod toolchaincheck go version.
templ CLIgo install github.com/a-h/templ/cmd/templ@latestmust be on PATH. The Makefile shells out to it.
Tailwind CLIvia pnpm/npx tailwindcss (root workspace)invoked by make build-ui.
makesystemdrives the build.
Running sirloinrepo make dev-up-d (or local sirloin)strip cannot start without STRIP_SIRLOIN_GRPC_HOST reachable.

First-time bootstrap

Terminal window
cd apps/strip
make deps # go mod download + tidy
make build-ui # templ generate -> tailwind -> asset bundler
make build # produces bin/strip

The asset bundler is a tiny Go program at cmd/build-assets/main.go that versions CSS/JS into internal/app/assets/.

Run

The Makefile gives you the minimum env:

Terminal window
make run-dev
# equivalent to:
# STRIP_STAGE=development \
# STRIP_PORT=:8080 \
# STRIP_SIRLOIN_GRPC_HOST=localhost:50051 \
# go run cmd/app/main.go

In development with no Clerk env, RequireAuth falls back to a fake dev_user and logs SECURITY WARNING: Skipping authentication. This is intentional for local dev. To exercise the real Clerk path locally, also set:

Terminal window
STRIP_CLERK_PUBLISHABLE_KEY=pk_test_...
STRIP_CLERK_DOMAIN=clerk.<your-test-domain>
STRIP_CLERK_SECRET_KEY=sk_test_...

To test as a user without re-auth, set STRIP_AUTH_BYPASS_UUID=<uuid> and request:

Terminal window
curl -H "X-Auth-Bypass: $STRIP_AUTH_BYPASS_UUID" http://localhost:8080/dashboard

See /services/strip-env/ for the full variable list.

Watch mode

Terminal window
make watch-ui # parallel templ generate --watch + tailwindcss --watch
# in a second terminal
make run # plain go run, no rebuild trigger

There is no built-in Go file-watcher. Use air, reflex, or your editor’s task runner if you want auto-restart on .go changes — templ and Tailwind are the slow parts and make watch-ui already handles them.

Build flow

flowchart LR
A[*.templ files] --> B[templ generate]
B --> C[*_templ.go]
D[input.css] --> E[tailwindcss -o output.css]
C --> F[go build]
E --> G[asset bundler]
G --> H[versioned assets]
F --> I[bin/strip]
H --> I

make build enforces this order. Skipping build-ui produces a binary that serves stale CSS or fails to render Templ pages.

Tests, lint, mocks

Terminal window
make run-tests # go test ./... -race
make test-coverage # coverage profile
make lint # golangci-lint v2 (excludes generated *_templ.go)

Project-wide line length is 180 and cyclomatic max 20 (see root CLAUDE.md). Tests must pass with -race.

Talking to a local sirloin

Default: STRIP_SIRLOIN_GRPC_HOST=localhost:50051. With the repo-wide Docker stack:

Terminal window
# from repo root
make dev-up-d
# strip will reach sirloin at sirloin:50051 inside the network,
# or localhost:50051 from your host if sirloin's port is exposed.

If sirloin is unreachable, every protected page returns 500 — Strip has no offline mode.

Gotchas

  • Templ files aren’t Go. *.templ must be regenerated after edits or you’ll see “function not defined” on build. make build-ui fixes it; CI runs templ generate too.
  • Tailwind purge. The Tailwind config scans internal/app/strip/templates/**/*.templ. New page files outside that path won’t pick up classes — add the path to the Tailwind config.
  • Lint excludes templates. golangci-lint skips internal/app/strip/templates/* (generated). Hand-written code in handlers/, services/, middleware/ is fully linted.
  • Auth bypass logs are loud. Every dev request without Clerk emits SECURITY WARNING. That’s expected locally — it must never appear in staging/prod logs.
  • Cookies require HTTPS in non-dev. __session is set with Secure: true whenever stage is not development. If you run staging locally without TLS, login appears to silently fail.
  • gRPC client is per-process. Editing STRIP_SIRLOIN_GRPC_HOST and HUP’ing won’t pick up the change — restart the process.
  • No DB writes from strip. All persistence flows through sirloin RPCs (StripService in proto/sirloin/v5/strip.proto). If you find yourself wanting database/sql in a handler, push the change into sirloin instead.

Further reading

  • /services/strip-api/ — full route/RPC surface.
  • /services/strip-datatable/, /services/strip-filters/ — shared Templ primitives.
  • /services/strip-runbook/ — operational behaviour.