agentsclimarketplace

Go backend

Skill Tanq16/claudex/skills/go-backend

Use when implementing Go backend logic - covers internal package architecture, error handling, HTTP servers, storage patterns, and OAuth authentication for CLI clients (browser/device/manual flows, not server-side web OAuth)From its SKILL.md

Install
npx -y skills add Tanq16/claudex --skill go-backend

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

11.1 KB, ~2.6k tokens by cl100k_base, as published. Nobody here has run it

Go Backend

Architecture and implementation patterns for Go internal packages.

When to Use

Use this skill when:

  • Structuring internal/ packages
  • Implementing business logic
  • Setting up HTTP servers
  • Designing storage/persistence layer
  • Handling errors across packages
  • Adding OAuth authentication to CLI tools

Requires: go-foundations for project layout and principles.

Web Only constraint: Internal packages in Web Only projects — and the server layer of CLI + Web hybrids — must NOT import utils/. Use log.Printf with manual level prefixes (e.g., log.Printf("ERROR ...")) and log.Fatal()/log.Fatalf() for fatal errors. Keep messages generic — no package-name prefix. (A hybrid's CLI-operation packages follow CLI Only conventions instead.)

Start here — required reading

Read the Always file now, in full, before building the server — it carries the canonical server structure you'll be held to. Read the When file before the sub-task it names; a subagent may read it if you delegate that work.

Always:

  • ./references/http-server-template.md — the canonical embedded-static net/http server (full server.go, skeleton, middleware)

When adding OAuth authentication to a CLI client:

  • ./references/auth-patterns.md — complete internal/auth/auth.go + cmd/login.go templates

Package Architecture

By Domain/Feature (Default)

internal/
├── auth/           # Authentication logic
├── download/       # Download functionality
└── server/         # HTTP server + frontend

By Layer (When Many Features)

When project has many features across multiple layers:

internal/
├── handlers/       # HTTP handlers by feature
│   ├── auth.go
│   └── download.go
├── services/       # Business logic by feature
│   ├── auth.go
│   └── download.go
├── models/         # Data structures
└── server/         # Server setup

Decision rule: Start with domain/feature. Switch to layered when you have 5+ features that each need handlers, services, and models.

Error Handling

Two Package Types

Task Packages (internal logic, potentially portable to pkg/):

  • Return errors as-is
  • Small, focused methods
  • No wrapping, no logging
// internal/download/client.go
func (c *Client) FetchFile(url string) ([]byte, error) {
    resp, err := c.httpClient.Get(url)
    if err != nil {
        return nil, err  // Return as-is
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        return nil, fmt.Errorf("unexpected status: %d", resp.StatusCode)
    }

    return io.ReadAll(resp.Body)
}

Interaction Packages (Cobra commands, HTTP handlers):

  • Wrap errors with context
  • Handle logging
  • User-facing error messages

CLI Only pattern (uses utils):

// cmd/download.go
Run: func(cmd *cobra.Command, args []string) {
    client := download.NewClient()
    data, err := client.FetchFile(url)
    if err != nil {
        u.PrintFatal(fmt.Sprintf("Failed to download from %s", url), err)
    }
    u.PrintSuccess("Download complete")
}

Web Only pattern (the interaction layer is HTTP handlers, shown in the HTTP Server Pattern section, which use log.Printf). For Cobra commands in a Web Only project (or a hybrid's serve command):

// cmd/download.go
Run: func(cmd *cobra.Command, args []string) {
    client := download.NewClient()
    data, err := client.FetchFile(url)
    if err != nil {
        log.Fatalf("ERROR Failed to download from %s: %v", url, err)
    }
    log.Printf("INFO Download complete")
}

Rationale

Task packages stay portable—if moved to pkg/ later, no changes needed. Context and logging happen at boundaries (commands, handlers).

HTTP Server Pattern

Use standard net/http (KISS principle) — no third-party routers (gin, chi, echo). A Server struct holds host, port, and an *http.ServeMux; Setup() mounts embedded static files and routes; Run() calls http.ListenAndServe.

//go:embed static
var staticFiles embed.FS

func (s *Server) Setup() error {
    staticFS, err := fs.Sub(staticFiles, "static")
    if err != nil {
        return err
    }
    s.mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(staticFS))))
    s.mux.HandleFunc("/api/health", s.handleHealth)
    s.mux.HandleFunc("/", s.handleIndex) // serves static/index.html
    return nil
}

go-backend owns the canonical embedded-static server — the full server.go (struct, New, Run, handleIndex, health handler), the skeleton variant, and the middleware wrapper pattern all live in ./references/http-server-template.md. go-frontend references that file rather than redefining the boilerplate.

Storage Pattern

Most projects: JSON/file-based, stateless.

When persistence needed, use interface abstraction:

// internal/storage/storage.go
package storage

type Store interface {
    Get(key string) ([]byte, error)
    Set(key string, value []byte) error
    Delete(key string) error
    List() ([]string, error)
}

// internal/storage/json.go
type JSONStore struct {
    path string
}

func NewJSONStore(path string) *JSONStore {
    return &JSONStore{path: path}
}

func (s *JSONStore) Get(key string) ([]byte, error) {
    // Read from JSON file
}

// internal/storage/postgres.go
type PostgresStore struct {
    db *sql.DB
}

func NewPostgresStore(connStr string) (*PostgresStore, error) {
    // Connect to Postgres
}

func (s *PostgresStore) Get(key string) ([]byte, error) {
    // Query Postgres
}

Usage

// In command or server setup
var store storage.Store

if usePostgres {
    store, err = storage.NewPostgresStore(connStr)
} else {
    store = storage.NewJSONStore(dataPath)
}

// Pass store to handlers/services
svc := myservice.New(store)

Authentication Pattern (CLI Only)

For CLI tools that authenticate with OAuth2 providers (Google, GitHub, Microsoft, etc.), use the three-mode login pattern in internal/auth/.

Three Login Modes

ModeFlagHow It WorksEnvironment
Callback (default)(none)Opens browser, localhost server receives redirectInteractive desktop
Device--device-loginShows URL + code, polls until authorizedHeadless / SSH / server
Manual--manualShows URL, user pastes authorization codeLast resort / no device flow support

The user explicitly selects their mode via flags — no auto-fallback chain. Default opens browser; if it fails on headless, the error message directs them to --device-login.

Key Design Decisions

  • Login(config, mode) takes a mode string — the command layer maps flags to mode, auth package handles the flow
  • openBrowser returns error — fast-fail on headless instead of hanging for 5 minutes
  • Device flow uses oauth2.Config.DeviceAuth() and DeviceAccessToken() — built into golang.org/x/oauth2, handles polling and backoff automatically
  • Manual flow accepts both bare codes and full URLs via extractCode() helper
  • Token persistence: ~/.config/[APP_NAME]/token.json with 0600 permissions
  • Auto-refresh on load: GetHTTPClient() loads cached token, refreshes if expired, saves if refreshed

Provider Support

Not all providers support device authorization (RFC 8628). When unsupported, omit loginWithDevice and the --device-login flag, keeping only callback and manual modes.

ProviderDevice AuthDevice Auth URL
GoogleYeshttps://oauth2.googleapis.com/device/code
MicrosoftYeshttps://login.microsoftonline.com/common/oauth2/v2.0/devicecode
GitHubYeshttps://github.com/login/device/code
Box.comNo

Output Tier Usage

Login flows use u.PrintInfo for instructions/status and u.PrintGeneric for data (URLs, codes). u.PromptInput in manual mode reads from pipe in --for-ai mode. u.PrintFatal and u.PrintSuccess are used at the command layer only.

Implementation

Use ./references/auth-patterns.md for the complete internal/auth/auth.go and cmd/login.go templates.

Config Struct Pattern

Define config structs that map to Cobra flags:

// internal/download/config.go
package download

type Config struct {
    URL         string
    OutputPath  string
    Concurrency int
    Timeout     time.Duration
}

// internal/download/downloader.go
func Download(cfg Config) error {
    // Use cfg fields
}

CLI Only (uses u.PrintFatal):

// cmd/download.go
Run: func(cmd *cobra.Command, args []string) {
    cfg := download.Config{
        URL:         downloadFlags.url,
        OutputPath:  downloadFlags.output,
        Concurrency: downloadFlags.concurrency,
        Timeout:     time.Duration(downloadFlags.timeout) * time.Second,
    }
    if err := download.Download(cfg); err != nil {
        u.PrintFatal("Download failed", err)
    }
}

Web Only (uses log.Fatalf):

// cmd/download.go
Run: func(cmd *cobra.Command, args []string) {
    cfg := download.Config{
        URL:         downloadFlags.url,
        OutputPath:  downloadFlags.output,
        Concurrency: downloadFlags.concurrency,
        Timeout:     time.Duration(downloadFlags.timeout) * time.Second,
    }
    if err := download.Download(cfg); err != nil {
        log.Fatalf("ERROR Download failed: %v", err)
    }
}

Common Patterns

HTTP Client with Defaults

// internal/httpclient/client.go
package httpclient

import (
    "net/http"
    "time"
)

func New() *http.Client {
    return &http.Client{
        Timeout: 30 * time.Second,
        Transport: &http.Transport{
            MaxIdleConns:        100,
            MaxIdleConnsPerHost: 10,
            IdleConnTimeout:     90 * time.Second,
        },
    }
}

Parallelization (Vanilla Goroutines)

Don't abstract—keep explicit:

func ProcessItems(items []string, concurrency int) error {
    sem := make(chan struct{}, concurrency)
    errCh := make(chan error, len(items))

    var wg sync.WaitGroup
    for _, item := range items {
        sem <- struct{}{} // Acquire (blocks if `concurrency` already running)
        wg.Go(func() {
            defer func() { <-sem }() // Release

            if err := processItem(item); err != nil {
                errCh <- err
            }
        })
    }

    wg.Wait()
    close(errCh)

    // Collect errors
    var errs []error
    for err := range errCh {
        errs = append(errs, err)
    }

    if len(errs) > 0 {
        return fmt.Errorf("%d items failed", len(errs))
    }
    return nil
}

References

FilePurpose
./references/http-server-template.mdCanonical embedded-static net/http server (full server.go, skeleton variant, middleware) — referenced by go-frontend
./references/auth-patterns.mdComplete OAuth auth templates (internal/auth/auth.go and cmd/login.go)

What ships with it: 2 files

15.1 KB alongside SKILL.md

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.