.claude/skills/navigator/SKILL.md
Working with the Navigator Go submodule for web server fixes and enhancements. Use when deployment plans require Navigator changes, config parsing issues arise, or new routing/proxy behavior is needed.
npx skillsauth add rubys/showcase navigatorInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Navigator is included as a Git submodule because showcase routinely needs Navigator fixes to implement deployment plans. Changes are tested in showcase context before being pushed upstream.
Location: navigator/ (Git submodule)
Language: Go
Purpose: Multi-tenant web server with framework independence
navigator/
├── cmd/navigator/ # Production Navigator implementation
├── internal/ # Modular packages
│ ├── config/ # Configuration loading and parsing
│ ├── server/ # HTTP handling, routing, static files
│ ├── auth/ # Authentication (htpasswd)
│ ├── process/ # Web app lifecycle management
│ └── proxy/ # Reverse proxy and Fly-Replay
└── docs/ # MkDocs documentation
Understanding this flow is essential for fixing config-related bugs:
types.go (mirrors YAML structure with yaml tags)parser.go (converts YAML to internal format)types.go (optimized internal representation)Common Bug Pattern: When adding config fields, developers often:
Config struct (internal)YAMLConfig structparser.go// From internal/server/handler.go ServeHTTP()
1. Health checks // BEFORE everything (bypasses auth)
2. Authentication // EARLY check (before routing)
3. Rewrites/redirects
4. CGI scripts
5. Reverse proxies
6. Static files
7. Maintenance mode
8. Web app proxy // Tenant routing
Security Critical: Health checks MUST come before auth. Auth MUST come before tenant routing.
Example: Health Check Config (Recent Fix)
Step 1: Add to YAMLConfig in internal/config/types.go:
type YAMLConfig struct {
Server struct {
// ... other fields
HealthCheck HealthCheckConfig `yaml:"health_check"` // ← Must have yaml tag
} `yaml:"server"`
}
Step 2: Add to internal Config (if different structure needed):
type Config struct {
Server struct {
// ... other fields
HealthCheck HealthCheckConfig
}
}
Step 3: Add parsing logic in internal/config/parser.go:
func (p *ConfigParser) parseServerConfig() {
// ... other parsing
p.config.Server.HealthCheck = p.yamlConfig.Server.HealthCheck // ← CRITICAL
}
Step 4: Add tests in internal/config/parser_test.go:
func TestConfigParser_ParseHealthCheck(t *testing.T) {
yamlConfig := YAMLConfig{}
yamlConfig.Server.HealthCheck = HealthCheckConfig{
Path: "/up",
Response: &HealthCheckResponse{Status: 200, Body: "OK"},
}
parser := NewConfigParser(&yamlConfig)
config, err := parser.Parse()
if config.Server.HealthCheck.Path != "/up" {
t.Errorf("HealthCheck not parsed correctly")
}
}
Step 5: Add integration tests in internal/server/handler_test.go (or appropriate file).
Example: Health Check Handler (Recent Fix)
// Add early in ServeHTTP - order matters!
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Health checks BEFORE authentication
if h.config.Server.HealthCheck.Path != "" && r.URL.Path == h.config.Server.HealthCheck.Path {
h.handleHealthCheck(recorder, r)
return // Stop processing
}
// Authentication check comes next
// ... rest of handler
}
Integration Test (Security Critical):
func TestHandler_ServeHTTP_HealthCheckBeforeAuth(t *testing.T) {
cfg := &config.Config{}
cfg.Server.HealthCheck = config.HealthCheckConfig{
Path: "/up",
Response: &config.HealthCheckResponse{Status: 200, Body: "OK"},
}
cfg.Auth.Enabled = true // Enable auth
basicAuth := &auth.BasicAuth{}
handler := CreateTestHandler(cfg, nil, basicAuth, nil)
req := httptest.NewRequest("GET", "/up", nil)
// NO auth credentials provided
recorder := httptest.NewRecorder()
handler.ServeHTTP(recorder, req)
// MUST succeed without auth - critical security requirement
if recorder.Code != 200 {
t.Errorf("Health check should bypass auth")
}
}
[ ] Config parser tests (internal/config/parser_test.go)
[ ] Integration tests (appropriate *_test.go)
ServeHTTP# All tests
go test ./...
# Specific package
go test -v ./internal/config/
go test -v ./internal/server/
# With coverage
go test -cover ./...
# Pre-commit validation (CI requirements)
gofmt -s -l . && \
golangci-lint run && \
go vet ./... && \
go test -race -cover -timeout=3m ./... && \
go build ./cmd/navigator-refactored && \
echo "✓ All CI checks passed!"
Problem: reload_config field in CGI scripts ignored
Cause: Field missing from YAMLConfig.Server.CGIScripts
Fix: Added field to CGIScriptConfig struct
Lesson: Always check YAMLConfig has all fields with yaml tags
Problem: /up returned 401 (auth) or started index tenant unnecessarily
Cause:
YAMLConfig.Server missing HealthCheck fieldparseServerConfig() not copying health check configFix:
// types.go - Added to YAMLConfig.Server
HealthCheck HealthCheckConfig `yaml:"health_check"`
// parser.go - Added to parseServerConfig()
p.config.Server.HealthCheck = p.yamlConfig.Server.HealthCheck
Test Coverage: 270 lines added
// ❌ BUG: Field exists in YAMLConfig but not copied
type YAMLConfig struct {
Server struct {
HealthCheck HealthCheckConfig `yaml:"health_check"`
}
}
func (p *ConfigParser) parseServerConfig() {
// ... other fields copied
// ❌ FORGOT: p.config.Server.HealthCheck = p.yamlConfig.Server.HealthCheck
}
// ✅ FIX: Add the copy statement
p.config.Server.HealthCheck = p.yamlConfig.Server.HealthCheck
How to catch: Write parser tests that verify field is copied.
// ❌ BUG: No yaml tag, field won't unmarshal
type ServerConfig struct {
Listen string // Won't unmarshal from YAML
}
// ✅ FIX: Add yaml tag
type ServerConfig struct {
Listen string `yaml:"listen"`
}
// ❌ BUG: Auth check after tenant routing (can be bypassed!)
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.handleWebAppProxy(w, r) // Routes first
if !h.auth.CheckAuth(r) { // Auth never runs!
return
}
}
// ✅ FIX: Correct order
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 1. Health checks (before everything)
if h.config.Server.HealthCheck.Path != "" { /* ... */ }
// 2. Authentication (EARLY)
isPublic := auth.ShouldExcludeFromAuth(r.URL.Path, h.config)
needsAuth := h.auth.IsEnabled() && !isPublic
if needsAuth && !h.auth.CheckAuth(r) {
h.auth.RequireAuth(recorder)
return
}
// 3. Then routing
h.handleWebAppProxy(w, r)
}
How to catch: Write integration tests that verify security (auth bypass scenarios).
cd navigator
# Fetch latest from remote
git fetch origin
# Rebase on main (creates a clean linear history)
git rebase origin/main
# If conflicts, resolve them and continue
# git rebase --continue
# Make changes, run tests
go test ./...
golangci-lint run
git add -A
git commit -m "Fix: health check parsing"
cd .. # Back to showcase root
# Test Navigator fix with showcase deployment
# Push Navigator changes
cd navigator
git push origin HEAD:refs/heads/main
# Update submodule reference in showcase
cd ..
git add navigator
git commit -m "Update navigator: fix health check parsing"
git push
Note: If submodule is in detached HEAD state (common after git submodule update), the rebase step ensures you're building on the latest main branch before making changes.
# Build Navigator
cd navigator
go build -o bin/navigator cmd/navigator
# Or use make
make build
# Run with config
./bin/navigator config/navigator.yml
# Reload config (SIGHUP)
kill -HUP $(cat /tmp/navigator.pid)
# Debug config loading
LOG_LEVEL=debug ./bin/navigator config/navigator.yml
You need Navigator changes when:
Pattern: Fix Navigator first, test in showcase, then continue deployment plan.
navigator/CLAUDE.md - Comprehensive development guide (read first!)internal/config/types.go - All config structures (YAMLConfig + Config)internal/config/parser.go - YAML → Config conversioninternal/server/handler.go - Main HTTP request routingnavigator/CLAUDE.md for comprehensive guidenavigator/docs/ for user documentationLOG_LEVEL=debug to see what's happeningdevelopment
Information about running tests, test coverage, and known testing issues in the Rails application. Use when the user asks about testing procedures, encounters test failures, wants to know about coverage, or needs to troubleshoot intermittent system test issues.
development
Use this skill to inspect what a Rails page currently displays, extract HTML content, or verify rendering WITHOUT starting a dev server. Useful for understanding page output before making changes, debugging views, searching for content, or testing that pages work correctly. Provides .claude/skills/render-page/scripts/render.rb for quick page inspection and HTML extraction.
testing
Use when asked to run "fly ssh console", SSH into Fly.io machines, inspect files on production machines, check processes on Fly.io, or examine deployed machine state. Covers critical pitfalls like no shell support and Debian vs macOS command differences.
development
Deployment architecture, multi-tenancy design, and environment-specific deployment commands. Use when the user asks about how the application is deployed, multi-tenant architecture with Navigator, Fly.io deployment, or frontend stack details.