golang-spf13-viper
Golang configuration library using spf13/viper — layered precedence (flag > env > file > KV > default), BindPFlag/BindPFlags, SetEnvPrefix + SetEnvKeyReplacer + AutomaticEnv, ReadInConfig + ConfigFileNotFoundError, Unmarshal + mapstructure struct tags, Sub for sub-trees, WatchConfig + OnConfigChange for hot reload, viper.New() for test isolation, and remote KV integration. Apply when using or adopting spf13/viper, or when the codebase imports `github.com/spf13/viper`. For CLI command structure alongside viper, see the `samber/cc-skills-golang@golang-spf13-cobra` skill. For general CLI architecture, see `samber/cc-skills-golang@golang-cli`.
Rank
62
Safety
84
Downloads
1.1k
Updated
Oct 11, 2026
Version
1.1.0
Source
CLAWHUB
About
What it does, and when to use it.
Capability contract not published. No trust telemetry is available yet. 1.1K downloads reported by the source. Last updated 10/11/2026.
Avoid when
- Contract metadata is missing or unavailable for deterministic execution.
Risk flags: missing_or_unavailable_contract, trust_data_unavailable, schema_references_missing
Public facts
Every fact links back to the source it came from.
- Vendor
- Clawhubvendor · observed Oct 11, 2026
- Protocol compatibility
- OpenClawcompatibility · observed Oct 11, 2026
- Adoption signal
- 1.1K downloadsadoption · observed Oct 11, 2026
- Latest release
- 1.1.0release · observed Aug 23, 2026
- Handshake status
- UNKNOWNsecurity
Install and run
Setup complexity: medium.
clawhub skill install s173arkhs3131fq5jf769qq75583hdgt:golang-spf13-viper- Install using `clawhub skill install s173arkhs3131fq5jf769qq75583hdgt:golang-spf13-viper` in an isolated environment before connecting it to live workloads.
- No published capability contract is available yet, so validate auth and request/response behavior manually.
- Review the upstream CLAWHUB listing at https://clawhub.ai/samber/golang-spf13-viper before using production credentials.
Contract: missing
curl -s "https://www.xpersona.co/api/v1/agents/clawhub-samber-golang-spf13-viper/snapshot"
Documentation
CLAWHUB
144,483 characters of source documentation, loaded on request.
Extracted files
5 files captured from the source.
SKILL.md
---
name: golang-spf13-viper
description: "Golang configuration library using spf13/viper — layered precedence (flag > env > file > KV > default), BindPFlag/BindPFlags, SetEnvPrefix + SetEnvKeyReplacer + AutomaticEnv, ReadInConfig + ConfigFileNotFoundError, Unmarshal + mapstructure struct tags, Sub for sub-trees, WatchConfig + OnConfigChange for hot reload, viper.New() for test isolation, and remote KV integration. Apply when using or adopting spf13/viper, or when the codebase imports `github.com/spf13/viper`. For CLI command structure alongside viper, see the `samber/cc-skills-golang@golang-spf13-cobra` skill. For general CLI architecture, see `samber/cc-skills-golang@golang-cli`."
user-invocable: true
license: MIT
compatibility: Designed for Claude Code, Codex or similar harness, and for projects using Golang.
metadata:
author: samber
version: "1.1.0"
openclaw:
emoji: "🔧"
homepage: https://github.com/samber/cc-skills-golang
requires:
bins:
- go
install: []
skill-library-version: "1.21.0"
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*) Bash(git:*) Agent WebFetch mcp__context7__resolve-library-id mcp__context7__query-docs Bash(godig:*) Bash(gopls:*) LSP mcp__gopls__*
paths:
- "**/*.go"
---
**Persona:** You are a Go engineer who treats configuration as a layered system. Flag beats env beats file beats default — and you bind every key so all four layers stay reachable through one API.
# Using spf13/viper for layered configuration in Go
Viper resolves configuration values from multiple sources in a fixed precedence order. It has no user-facing surface — it doesn't define commands or flags. Its job is to answer "what is the value of key X right now?" by walking its source layers from highest to lowest priority.
**Official Resources:**
- [pkg.go.dev/github.com/spf13/viper](https://pkg.go.dev/github.com/spf13/viper)
- [github.com/spf13/viper](https://github.com/spf13/viper)
This skill is not exhaustive. Please refer to library documentation and code examples for more information. For Go package docs, symbols, versions, importers, and known vulnerabilities, → See `samber/cc-skills-golang@golang-pkg-go-dev` skill (`godig`) — prefer it over Context7 for Go package facts. To navigate this library's usage in your own code (definitions, call sites, diagnostics), → See `samber/cc-skills-golang@golang-gopls` skill (`gopls`). Context7 remains a fallback for docs not indexed on pkg.go.dev.
```bash
go get github.com/spf13/viper@latest
```
## Viper vs. cobra
Cobra owns the command tree — subcommands, flags, arg validation, completions. Viper owns configuration resolution — it answers "what is the value of key X?" by walking its source layers. Viper has no user-facing surface; it is purely a key-value resolver. Use cobra alone for flag-only CLIs; viper alone for config-file daemons; both when you need both, binding flags at `PersistentPreRunE` via `BindPFlag`.
→ See `samber/cc-skills-gol_meta.json
{
"ownerId": "kn72rhnkwjfeex9wr1n7y24qa983cjn3",
"slug": "golang-spf13-viper",
"version": "1.1.0",
"publishedAt": 1787452433233
}references/binding-and-env.md
# Viper Env Binding and Flag Binding
## The binding interaction model
Three settings control how viper maps environment variables to keys. They must be set together:
```go
viper.SetEnvPrefix("MYAPP") // adds MYAPP_ prefix
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) // database.host → MYAPP_DATABASE_HOST
viper.AutomaticEnv() // activates auto-binding
```
Call these before any `ReadInConfig` or `viper.Get*` call — typically in a root command's `PersistentPreRunE` or in `init()`.
## AutomaticEnv vs BindEnv
| Method | Behavior |
| --- | --- |
| `AutomaticEnv()` | Every key is automatically mapped to its env equivalent (with prefix and replacer applied) |
| `BindEnv(key, envVars...)` | Only the specified key is bound, to the specified env var name(s) |
Use `AutomaticEnv` for the common case. Use `BindEnv` when you need to bind to an env var with a name that doesn't follow your prefix/replacer convention (e.g., third-party env vars like `GOOGLE_APPLICATION_CREDENTIALS`).
```go
// Bind a specific non-prefixed env var
viper.BindEnv("google.credentials", "GOOGLE_APPLICATION_CREDENTIALS")
```
## SetEnvKeyReplacer in depth
Viper keys use `.` as separator for nested values. Env vars cannot contain dots. The replacer maps between them.
```go
// Config file:
// database:
// host: localhost
// max_conn: 25
// Without replacer:
viper.SetEnvPrefix("MYAPP")
viper.AutomaticEnv()
viper.GetString("database.host") // looks for MYAPP_DATABASE.HOST — no match
// With replacer:
viper.SetEnvPrefix("MYAPP")
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
viper.AutomaticEnv()
viper.GetString("database.host") // looks for MYAPP_DATABASE_HOST — matches
```
The replacer operates on the viper key **before** prepending the prefix, so the lookup chain is: `database.host` → replace `.` with `_` → `database_host` → prepend prefix → `MYAPP_DATABASE_HOST`.
## AllowEmptyEnv
By default, viper ignores env vars set to the empty string — the empty string is treated as "not set" and viper continues down the precedence stack. Override this behavior:
```go
viper.AllowEmptyEnv(true)
// now MYAPP_PORT="" → viper.GetInt("port") == 0, not the default
```
## Flag binding
Bind a pflag after defining it:
```go
func init() {
rootCmd.PersistentFlags().Int("port", 8080, "listen port")
viper.BindPFlag("port", rootCmd.PersistentFlags().Lookup("port"))
}
```
Bind an entire flag set:
```go
viper.BindPFlags(rootCmd.PersistentFlags())
```
**Timing rule:** Bind flags in `init()` or in `PersistentPreRunE`. The binding call must happen before `Execute()` parses flags — specifically, before any `viper.Get*` call on a flag-backed key. Binding after `Execute()` causes the flag's `Changed` state to be unknown, so viper may not promote the flag value to the correct precedence layer.
## How pflag binding interacts with precedence
Viper checks `flag.Changed` (whether the user explicitly passed the flagreferences/sources-and-formats.md
# Viper Config Sources and File Formats
## Supported file formats
Viper detects format from file extension. Supported extensions:
| Format | Extensions |
| ---------- | --------------- |
| YAML | `.yaml`, `.yml` |
| TOML | `.toml` |
| JSON | `.json` |
| HCL | `.hcl` |
| INI | `.ini` |
| Properties | `.properties` |
| dotenv | `.env` |
Force a format when there is no extension:
```go
viper.SetConfigType("yaml")
viper.SetConfigFile("/etc/myapp/config") // no extension — type required
```
## Config file search
```go
viper.SetConfigName("config") // file name without extension
viper.SetConfigType("yaml") // required when no extension
viper.AddConfigPath("$HOME/.myapp") // search path 1 (highest priority when multiple)
viper.AddConfigPath("/etc/myapp/") // search path 2
viper.AddConfigPath(".") // search path 3 (lowest priority)
// viper searches paths in order, stops at the first match
if err := viper.ReadInConfig(); err != nil {
var notFound *viper.ConfigFileNotFoundError
if !errors.As(err, ¬Found) {
return err // real error (permission denied, malformed YAML, etc.)
}
// not found — continue with flags/env/defaults
}
// After reading, this returns the resolved path:
fmt.Println("Using config:", viper.ConfigFileUsed())
```
## Merging multiple config files
`MergeInConfig` merges a second config file into the current state. Later values override earlier ones for the same key.
```go
viper.SetConfigFile("base.yaml")
viper.ReadInConfig()
viper.SetConfigFile("override.yaml")
viper.MergeInConfig() // keys from override.yaml win on collision
```
Pattern: ship a base config with the binary, let users drop an override in `~/.myapp/override.yaml`.
## Multiple config files via SetConfigFile
For environment-based config loading:
```go
env := os.Getenv("APP_ENV")
if env == "" {
env = "development"
}
viper.SetConfigFile(fmt.Sprintf("config.%s.yaml", env))
viper.ReadInConfig()
```
## Remote KV stores (etcd, Consul)
Viper supports remote KV stores via the `viper/remote` sub-package. This keeps remote config behind an opt-in import:
```go
import _ "github.com/spf13/viper/remote"
// etcd
viper.AddRemoteProvider("etcd3", "http://127.0.0.1:2379", "/config/myapp.yaml")
viper.SetConfigType("yaml")
viper.ReadRemoteConfig()
// Consul
viper.AddRemoteProvider("consul", "localhost:8500", "myapp/config")
viper.SetConfigType("json")
viper.ReadRemoteConfig()
```
**Caution:** Remote config adds network latency to startup and a runtime dependency. Use it only when you need centralized config across many service instances. For most applications, files + env vars are sufficient.
Watch for remote changes:
```go
go func() {
for {
time.Sleep(5 * time.Second)
viper.WatchRemoteConfig()
// re-read values after watching
}
}()
```
## Embedding config with go:embedreferences/testing-and-isolation.md
# Viper Test Isolation
## The global state problem
The top-level `viper.*` functions operate on a global `*viper.Viper` instance shared across all tests in the same process. Tests that call `viper.SetConfigFile`, `viper.Set`, or `viper.ReadInConfig` pollute this global state, causing flaky test ordering.
```go
// ✗ Bad — sets global state that affects later tests
func TestPortConfig(t *testing.T) {
viper.SetDefault("port", 8080)
viper.Set("port", 9090)
assert.Equal(t, 9090, viper.GetInt("port"))
// global viper now has port=9090 for all subsequent tests
}
```
## viper.New() per test (correct approach)
```go
func TestPortConfig(t *testing.T) {
v := viper.New()
v.SetDefault("port", 8080)
v.Set("port", 9090)
assert.Equal(t, 9090, v.GetInt("port"))
}
func TestDefaultPort(t *testing.T) {
v := viper.New()
v.SetDefault("port", 8080)
assert.Equal(t, 8080, v.GetInt("port")) // clean — not affected by TestPortConfig
}
```
## Injecting viper into your app
For test isolation to work, your application code must accept a `*viper.Viper` instead of calling the global functions directly:
```go
// ✓ Good — accepts a viper instance
type Server struct {
cfg *viper.Viper
}
func NewServer(v *viper.Viper) *Server {
return &Server{cfg: v}
}
func (s *Server) Port() int {
return s.cfg.GetInt("port")
}
// In tests:
func TestServer(t *testing.T) {
v := viper.New()
v.Set("port", 9090)
s := NewServer(v)
assert.Equal(t, 9090, s.Port())
}
// In main:
func main() {
// viper setup...
s := NewServer(viper.GetViper()) // pass the global instance in production
}
```
## Reading config files in tests
```go
func TestReadConfig(t *testing.T) {
v := viper.New()
v.SetConfigFile("testdata/config.yaml")
require.NoError(t, v.ReadInConfig())
assert.Equal(t, "localhost", v.GetString("host"))
}
```
Use `testdata/` for config files. Go test tooling sets the working directory to the package directory, so relative paths work reliably.
## t.Setenv interactions
`t.Setenv` sets an env var for the duration of a test and restores it on cleanup. Combined with `viper.New()` + `AutomaticEnv`, this lets you test env var binding without global pollution:
```go
func TestEnvBinding(t *testing.T) {
t.Setenv("MYAPP_PORT", "9090")
v := viper.New()
v.SetEnvPrefix("MYAPP")
v.AutomaticEnv()
assert.Equal(t, 9090, v.GetInt("port"))
// t.Setenv restores original MYAPP_PORT (or unsets it) after this test
}
```
## viper.Reset() — use with caution
`viper.Reset()` resets the global viper instance to its zero state. It is rarely the right solution:
- It affects all code running concurrently that also uses the global viper.
- It does not stop any active `WatchConfig` goroutines.
- Using it in `TestMain` or `t.Cleanup` makes tests order-dependent.
Prefer `viper.New()` per test. Reserve `Reset()` for tools that call into viper-based libraries and must restore state between runs.
#AionUi
Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!
activepieces
AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents
cherry-studio
AI productivity studio with smart chat, autonomous agents, and 300+ assistants.
CopilotKit
The Frontend for Agents & Generative UI. React + Angular
Machine-readable data
The same record, as JSON, for agents and crawlers.
{
"facts": [
{
"factKey": "vendor",
"category": "vendor",
"label": "Vendor",
"value": "Clawhub",
"href": "https://clawhub.ai/samber/skills/golang-spf13-viper",
"sourceUrl": "https://clawhub.ai/samber/skills/golang-spf13-viper",
"sourceType": "profile",
"confidence": "medium",
"observedAt": "2026-10-11T08:59:02.243Z",
"isPublic": true
},
{
"factKey": "protocols",
"category": "compatibility",
"label": "Protocol compatibility",
"value": "OpenClaw",
"href": "https://www.xpersona.co/api/v1/agents/clawhub-samber-golang-spf13-viper/contract",
"sourceUrl": "https://www.xpersona.co/api/v1/agents/clawhub-samber-golang-spf13-viper/contract",
"sourceType": "contract",
"confidence": "medium",
"observedAt": "2026-10-11T08:59:02.243Z",
"isPublic": true
},
{
"factKey": "traction",
"category": "adoption",
"label": "Adoption signal",
"value": "1.1K downloads",
"href": "https://clawhub.ai/samber/golang-spf13-viper",
"sourceUrl": "https://clawhub.ai/samber/golang-spf13-viper",
"sourceType": "profile",
"confidence": "medium",
"observedAt": "2026-10-11T08:59:02.243Z",
"isPublic": true
},
{
"factKey": "latest_release",
"category": "release",
"label": "Latest release",
"value": "1.1.0",
"href": "https://clawhub.ai/samber/golang-spf13-viper",
"sourceUrl": "https://clawhub.ai/samber/golang-spf13-viper",
"sourceType": "release",
"confidence": "medium",
"observedAt": "2026-08-23T02:33:53.233Z",
"isPublic": true
},
{
"factKey": "handshake_status",
"category": "security",
"label": "Handshake status",
"value": "UNKNOWN",
"href": "https://www.xpersona.co/api/v1/agents/clawhub-samber-golang-spf13-viper/trust",
"sourceUrl": "https://www.xpersona.co/api/v1/agents/clawhub-samber-golang-spf13-viper/trust",
"sourceType": "trust",
"confidence": "medium",
"observedAt": null,
"isPublic": true
}
],
"events": [
{
"eventType": "release",
"title": "Release 1.1.0",
"description": "golang-spf13-viper 1.1.0 - Added compatibility with Claude Code, Codex, and similar harnesses. - Expanded metadata: new allowed tools (including godig, gopls, LSP), and specified explicit file patterns. - Guidance improved: Points to `golang-pkg-go-dev` skill for package docs/symbols/vulnerabilities and `golang-gopls` for code navigation. - Removed obsolete `skill-card.md` file. - Minor copy clarifications and rewording for tips on documentation lookup, making best practices and advisory links more visible.",
"href": "https://clawhub.ai/samber/golang-spf13-viper",
"sourceUrl": "https://clawhub.ai/samber/golang-spf13-viper",
"sourceType": "release",
"confidence": "medium",
"observedAt": "2026-08-23T02:33:53.233Z",
"isPublic": true
}
]
}Record generated Oct 11, 2026.
