Go application blueprint
  • Go 79.5%
  • Just 7.2%
  • Makefile 7%
  • HTML 3.3%
  • CSS 1.1%
  • Other 1.9%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Oleksii Khilkevych 33c1268e0d
All checks were successful
CI / Test & Build (push) Successful in 3m10s
CI / Release image (push) Has been skipped
CI / Dev image (push) Successful in 1m16s
Document the CLI tree, genericized from kpr
2026-10-18 13:01:54 +02:00
.claude Bless 2025-12-09 08:14:22 +01:00
.forgejo/workflows Absorb kpr's template-shaped decisions, genericized 2026-10-17 19:22:08 +02:00
.github Absorb kpr's template-shaped decisions, genericized 2026-10-17 19:22:08 +02:00
cmd/app Backport kpr bootstrap, genericized for the template 2026-09-27 16:43:42 +02:00
contrib Backport kpr bootstrap, genericized for the template 2026-09-27 16:43:42 +02:00
docs Document the CLI tree, genericized from kpr 2026-10-18 13:01:54 +02:00
internal Move Go runtime facts from top card to Configuration 2026-10-17 22:36:57 +02:00
LICENSES Absorb kpr's template-shaped decisions, genericized 2026-10-17 19:22:08 +02:00
observability Backport env screen, telemetry, and observability stack, generalized 2026-09-27 18:22:19 +02:00
scripts Absorb kpr's template-shaped decisions, genericized 2026-10-17 19:22:08 +02:00
site Absorb kpr's template-shaped decisions, genericized 2026-10-17 19:22:08 +02:00
.dockerignore Absorb kpr's template-shaped decisions, genericized 2026-10-17 19:22:08 +02:00
.gitignore Absorb kpr's template-shaped decisions, genericized 2026-10-17 19:22:08 +02:00
.golangci.yml Close coverage gaps to 98.6%, gate complexity at 15 2026-10-17 20:12:58 +02:00
AGENTS.md Keep doc titles out of generated TOCs 2026-10-17 23:28:57 +02:00
BUILD.md Backport shovel-ng docs batch as generic skeletons 2026-10-17 23:44:35 +02:00
CODE_OF_CONDUCT.md Absorb kpr's template-shaped decisions, genericized 2026-10-17 19:22:08 +02:00
codecov.yml Absorb kpr's template-shaped decisions, genericized 2026-10-17 19:22:08 +02:00
CONTRIBUTING.md Absorb kpr's template-shaped decisions, genericized 2026-10-17 19:22:08 +02:00
docker-compose.observability.yml Backport env screen, telemetry, and observability stack, generalized 2026-09-27 18:22:19 +02:00
docker-compose.yml Absorb kpr's template-shaped decisions, genericized 2026-10-17 19:22:08 +02:00
Dockerfile Default Website to the go-skel repo home 2026-10-17 22:34:45 +02:00
Dockerfile.package Absorb kpr's template-shaped decisions, genericized 2026-10-17 19:22:08 +02:00
go.mod Backport env screen, telemetry, and observability stack, generalized 2026-09-27 18:22:19 +02:00
go.sum Backport env screen, telemetry, and observability stack, generalized 2026-09-27 18:22:19 +02:00
justfile Default Website to the go-skel repo home 2026-10-17 22:34:45 +02:00
LICENSE Absorb kpr's template-shaped decisions, genericized 2026-10-17 19:22:08 +02:00
license.go Absorb kpr's template-shaped decisions, genericized 2026-10-17 19:22:08 +02:00
license_test.go Absorb kpr's template-shaped decisions, genericized 2026-10-17 19:22:08 +02:00
Makefile Default Website to the go-skel repo home 2026-10-17 22:34:45 +02:00
README.md Absorb kpr's template-shaped decisions, genericized 2026-10-17 19:22:08 +02:00
SECURITY.md Absorb kpr's template-shaped decisions, genericized 2026-10-17 19:22:08 +02:00

Go Skel

Go application blueprint with embedded management console and application server. A skeleton project for building production-ready Go applications with web UI and API endpoints. Fork it, rename the module and binary, and grow your domain inside internal/.

Features

  • Three-Port Architecture
    • Management console on port 9300 (metrics, configuration, health)
    • Application server on port 8080 (SPA + API endpoints)
    • Internal events on port 9000 (peer deliveries; unpublished, see below)
  • Embedded Assets: Static files compiled into the binary
  • Clean Structure: Separated concerns for management and application logic
  • Production Ready: Dual-stack IPv4/IPv6, proper timeouts, logging
  • Zero Dependencies: Pure Go stdlib for web serving
  • Cross-Platform: Builds for Linux, macOS (Intel & ARM)

Project Structure

go-skel/
├── cmd/app/              # Application entry point
├── internal/
│   ├── cli/              # Cobra commands (serve, flags)
│   ├── config/           # Env-backed settings, build identity, runtime facts
│   ├── console/          # Management console (port 9300)
│   │   ├── server.go     # HTTP server setup
│   │   ├── handlers.go   # Metrics, config, health endpoints
│   │   ├── assets.go     # Embedded dashboard template + stylesheet
│   │   └── static/       # Dashboard HTML and CSS (embedded)
│   ├── app/              # Application server (port 8080)
│   │   ├── server.go     # HTTP server setup
│   │   ├── handlers.go   # API endpoints
│   │   ├── assets.go     # Static file embedding
│   │   └── static/       # SPA assets (HTML, CSS, JS)
│   ├── events/           # Internal events port (9000, noop today)
│   │   ├── server.go     # HTTP server setup
│   │   └── events.go     # POST /events delivery handler
│   └── otel/             # OpenTelemetry setup (opt-in)
├── docs/                 # CONFIG, TESTING, goals
├── Makefile              # Build automation
├── justfile              # Alternative build tool (just)
└── go.mod                # Go module definition

Installation

From Source

Requires Go 1.27+:

# Clone the repository
git clone https://nrtn.dev/catalyst/go-skel.git
cd go-skel

# Build the binary
make build
# or
just build

# Run locally
./go-skel serve

Build for Multiple Platforms

# Build for all platforms
make build-all

# Or specific platform
GOOS=linux GOARCH=amd64 go build -o go-skel-linux-amd64 ./cmd/app

Supported platforms:

  • linux/amd64, linux/arm64, linux/arm
  • darwin/amd64, darwin/arm64

Usage

Start Both Servers

# Start management console (9300) and app server (8080)
go-skel serve

# Custom ports
go-skel serve --management-port 9300 --app-port 8080

# Custom hosts
go-skel serve --management-host :: --app-host 0.0.0.0

Environment Variables

# Management console
export GO_SKEL_MANAGEMENT_HOST=::     # Default: :: (dual-stack)
export GO_SKEL_MANAGEMENT_PORT=9300   # Default: 9300

# Application server
export GO_SKEL_APP_HOST=::            # Default: :: (dual-stack)
export GO_SKEL_APP_PORT=8080          # Default: 8080

go-skel serve

Endpoints

Management Console (Port 9300)

  • Dashboard: http://localhost:9300

    • Application metrics (uptime, requests, Go version)
    • Configuration viewer (version, commit, build time)
    • Environment variables
  • Metrics: http://localhost:9300/metrics

    {
      "uptime": "1h23m45s",
      "uptime_seconds": 5025,
      "requests": 42,
      "go_version": "go1.25.4",
      "version": "v1.0.0",
      "commit": "abc123",
      "build_time": "2025-12-08T09:30:00Z",
      "environment": { ... }
    }
    
  • Health: http://localhost:9300/health

    • Returns 200 OK if service is healthy

Application Server (Port 8080)

  • SPA: http://localhost:8080

    • Single page application with embedded assets
    • Served from internal/app/static/
  • API Endpoints:

    • GET /api/hello - Simple hello response

      {
        "message": "Hello from go-skel!",
        "timestamp": "2025-12-08T09:30:00Z",
        "version": "1.0.0"
      }
      
    • GET /api/data - Example data endpoint

      {
        "items": ["Item 1", "Item 2", ...],
        "count": 5,
        "timestamp": "2025-12-08T09:30:00Z"
      }
      

Internal Events (Port 9000)

  • POST /events - Accepts one delivery, answers 202, keeps nothing (a noop standing in for cluster-peer and machine-sender traffic)
  • GET /health - Returns 200 OK if the port is serving

The port stays unpublished in the dev stacks: peers reach it over the shared compose network, and nothing else needs it. It speaks plain HTTP today, but the port is the seam — a future gRPC or peer-gossip transport slots in behind the same bind without touching serve, config, or the compose topology.

Dev stack

Local run via compose:

docker compose up --build
  • go-skel: :8080 (app), :9300 (management console), :9000 (internal events — not published to the host)

Tracing backends (Jaeger/Tempo) are sketched as commented services in docker-compose.yml — uncomment what you need.

Development

Building

# Build current platform
make build

# Build all platforms
make build-all

# Build with version info
VERSION=v1.0.0 make build

Testing

# Run tests (gotestsum summary when installed, plain go test otherwise)
make test

# With coverage (terminal table + coverage.out + coverage.html)
make coverage

# Reprint the last coverage table without re-running tests
make coverage-report

# Linux-container suite, benchmarks, full gate
make test-linux
make bench
make check

Testing approach, coverage, and CI integration are documented in docs/TESTING.md; configuration in docs/CONFIG.md.

Code Quality

# Format code
make fmt

# Lint
make lint

# Run all checks
make check

Running Locally

# Run with go run
make run

# Or directly
go run ./cmd/app serve

OpenTelemetry (Optional)

go-skel includes built-in OpenTelemetry support for distributed tracing, disabled by default.

Enable OTEL

# Enable tracing
export OTEL_ENABLED=true
export OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317

# Run with tracing
go-skel serve

Quick Setup with Jaeger

# Start Jaeger
docker run -d --name jaeger \
  -p 16686:16686 -p 4317:4317 \
  jaegertracing/all-in-one:latest

# Enable OTEL and run
export OTEL_ENABLED=true
go-skel serve

# View traces at http://localhost:16686

Configuration

  • OTEL_ENABLED - Enable/disable (default: false)
  • OTEL_SERVICE_NAME - Service name (default: go-skel)
  • OTEL_EXPORTER_OTLP_ENDPOINT - OTLP endpoint (default: localhost:4317)
  • OTEL_ENVIRONMENT - Environment (default: development)
  • OTEL_SERVICE_VERSION - Version (default: dev)

See internal/otel/README.md for detailed configuration and cloud provider setup.

Customization

Adding New API Endpoints

Edit internal/app/handlers.go:

func YourHandler(w http.ResponseWriter, r *http.Request) {
    // Your logic here
    json.NewEncoder(w).Encode(response)
}

Register in internal/app/server.go:

mux.HandleFunc("/api/your-endpoint", YourHandler)

Modifying the SPA

Static assets are in internal/app/static/:

  • index.html - Main HTML
  • style.css - Styling
  • app.js - JavaScript

Assets are embedded at build time using go:embed.

Adding Management Console Metrics

Edit internal/console/handlers.go to add custom metrics (dashboard markup lives in internal/console/static/):

type metricsData struct {
    // Add your custom fields
    CustomMetric string `json:"custom_metric"`
}

Deployment

Systemd Service

See contrib/systemd/ for systemd service files and installation instructions.

# Quick install
sudo cp contrib/systemd/go-skel.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now go-skel

Docker

go-skel uses a two-stage build approach: compile binaries with make/just (fast, cached) and package with Docker (simple, consistent).

Quick Start

# Build the image for current platform
make docker-build
# or
just docker-build

# Run the container
make docker-run

Build Configuration

Docker builds:

  • Automatically tag as latest for version tags (e.g., v1.0.0)
  • Use dev version tag for non-release builds
  • Support multiplatform builds (amd64, arm64, armv7)
  • Package pre-built binaries from dist/ directory

Build from Source

# Build for current platform (builds binary + packages in Docker)
make docker-build

# Build with custom version
VERSION=v1.0.0 make docker-build

# Build binary only (outputs to dist/)
make build

Multiplatform Build

Build for multiple architectures (requires Docker Buildx):

# Build and push multiplatform image (builds all binaries first)
make docker-build-multiplatform

Supported platforms:

  • linux/amd64 (x86_64)
  • linux/arm64 (ARM64/AArch64)
  • linux/arm/v7 (ARMv7)

Run with Environment Variables

docker run --rm -p 8080:8080 -p 9300:9300 \
  -e OTEL_ENABLED=true \
  -e OTEL_EXPORTER_OTLP_ENDPOINT=tempo:4317 \
  -e GO_SKEL_MANAGEMENT_HOST=0.0.0.0 \
  -e GO_SKEL_APP_HOST=0.0.0.0 \
  nrtn.dev/catalyst/go-skel:latest

Docker Compose

version: '3.8'
services:
  go-skel:
    image: nrtn.dev/catalyst/go-skel:latest
    ports:
      - "8080:8080"
      - "9300:9300"
    environment:
      - OTEL_ENABLED=false
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:9300/health"]
      interval: 30s
      timeout: 3s
      retries: 3

Image Details

  • Base Image: Alpine Linux (minimal footprint)
  • Size: ~15-20 MB (compressed)
  • User: Runs as non-root user go-skel (UID 1000)
  • Security: No capabilities, read-only filesystem compatible
  • Health Check: Built-in health check on port 9300

License

MIT — see LICENSE. The binary embeds the full text: go-skel license prints it, so containers and air-gapped hosts carry it without a checkout. Third-party attributions ship in the image under /app/licenses (generated by make licenses, see LICENSES).

Contributing

Contributions welcome! This is a blueprint/skeleton project designed to be forked and customized for your needs.

Architecture Notes

Why Two Servers?

  • Separation of Concerns: Management operations separate from application logic
  • Security: Management console can be firewalled separately
  • Different Audiences: Ops teams use management console, users use application
  • Independent Scaling: Can run management console on a single instance

Embedded Assets

Static files are compiled into the binary using Go's embed package. This means:

  • Single binary deployment
  • No external file dependencies
  • Faster startup (no disk I/O for assets)
  • Easier containerization

Future Enhancements

This blueprint can be extended with:

  • Database connection management (PostgreSQL, MySQL, etc.)
  • Authentication/authorization middleware (JWT, OAuth)
  • Prometheus metrics integration
  • ✅ OpenTelemetry tracing (included, disabled by default)
  • gRPC API endpoints
  • WebSocket support
  • GraphQL API
  • Rate limiting and circuit breakers