Skip to content

Commit c9d6fba

Browse files
committed
Update docs: sync llms.txt to v1.0.1 API, fix SECURITY.md and AGENTS.md
1 parent 9067266 commit c9d6fba

5 files changed

Lines changed: 247 additions & 62 deletions

File tree

AGENTS.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ go test ./...
7171
golangci-lint run
7272
```
7373

74+
**Check for vulnerabilities:**
75+
76+
```bash
77+
go install golang.org/x/vuln/cmd/govulncheck@latest
78+
govulncheck ./...
79+
```
80+
7481
## Code Style & Principles
7582

7683
- **Core is iron:** The five core pieces (loading, defaulting, validation, errors, types) must be rock solid. Don't add
@@ -89,9 +96,9 @@ golangci-lint run
8996

9097
Be very careful when modifying these (breaking changes require major version bump):
9198

92-
- `Load[T](...Source) (T, error)`
93-
- `LoadWithOptions[T](...Option) (T, error)`
94-
- `LoadWithWatcher[T](filePath string, ...Source) (T, *ConfigWatcher, error)`
99+
- `Load[T](...Source) (*T, error)`
100+
- `LoadWithOptions[T](...Option) (*T, error)`
101+
- `LoadWithWatcher[T](filePath string, ...Source) (*T, *ConfigWatcher, error)`
95102
- `Explain(err error) string`
96103
- `Source` interface (`Name()`, `Lookup(*FieldInfo)`)
97104
- `FieldInfo` type
@@ -137,7 +144,7 @@ confkit is well-suited for Go services that use LLMs:
137144
type LLMConfig struct {
138145
Provider string `env:"LLM_PROVIDER" validate:"oneof=claude,openai,anthropic"`
139146
APIKey string `env:"LLM_API_KEY" secret:"true" validate:"required"`
140-
Model string `env:"LLM_MODEL" default:"claude-3-5-sonnet"`
147+
Model string `env:"LLM_MODEL" default:"claude-sonnet-4-6"`
141148
Temperature float64 `env:"LLM_TEMPERATURE" default:"0.7" validate:"min=0,max=1"`
142149
MaxTokens int `env:"LLM_MAX_TOKENS" default:"4096"`
143150
}

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ All notable changes to confkit are documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.0.1] - 2026-05-12
9+
10+
### Fixed
11+
12+
- **email validator**: replaced `net/mail.ParseAddress` with a simple regex to eliminate GO-2026-4977 and GO-2026-4986 (quadratic string concatenation in `net/mail`). Behaviour is unchanged for well-formed addresses.
13+
- **code formatting**: applied `gofmt` to `validation.go` and `schema/schema.go`.
14+
15+
---
16+
817
## [1.0.0] - 2026-05-10
918

1019
### Added

SECURITY.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -226,14 +226,15 @@ These are only required if you use the corresponding source.
226226

227227
We use:
228228

229-
- **Go's built-in vulnerability scanner:** `go list -json -m all | nancy sleuth`
229+
- **govulncheck:** `govulncheck ./...` — checks only reachable call paths, not just module imports
230230
- **GitHub Dependabot:** Automated PR for dependency updates
231231
- **Manual audits** before releases
232232

233233
Run locally:
234234

235235
```bash
236-
go list -json -m all | nancy sleuth
236+
go install golang.org/x/vuln/cmd/govulncheck@latest
237+
govulncheck ./...
237238
```
238239

239240
---
@@ -260,10 +261,10 @@ Security fixes are released for:
260261

261262
| Version | Status | Support |
262263
|---------|--------|------------------------|
263-
| v0.5.x | Active | Current + bug fixes |
264-
| v0.4.x | Stable | Security fixes only |
265-
| v0.3.x | Legacy | Critical security only |
266-
| < v0.3 | EOL | No support |
264+
| v1.0.x | Active | Current + bug fixes |
265+
| v0.10.x | Stable | Security fixes only |
266+
| v0.5.x | Legacy | Critical security only |
267+
| < v0.5 | EOL | No support |
267268

268269
**Recommendation:** Upgrade to latest stable version regularly.
269270

docs/llms.txt

Lines changed: 116 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
1-
# confkit
1+
# confkit — Type-Safe Configuration Toolkit for Go
22

3-
> Typed, validated configuration loading for Go. The Pydantic equivalent for Go services.
3+
Typed, validated configuration loading for Go. The Pydantic equivalent for Go services.
44

55
confkit loads configuration from multiple sources (YAML, JSON, TOML, env, flags, Kubernetes, Vault, Consul, etcd, AWS), merges them with explicit precedence, validates fields using struct tags, and returns a fully typed struct — or a human-readable error message identifying the exact field, source, and rule that failed.
66

7-
Use confkit instead of Viper when you need: type-safe config without Get*() accessors, validation at startup, secret redaction, or clear error messages. Use instead of raw os.Getenv when you need struct-level config with defaults and validation.
7+
**Use confkit instead of Viper** when you need: type-safe config without Get*() accessors, validation at startup, secret redaction, or clear error messages.
8+
9+
**Use confkit instead of raw os.Getenv** when you need struct-level config with defaults and validation.
10+
11+
## Key Differentiators
12+
13+
- ✅ **Typed Load[T]** — Returns your struct, fully type-checked at compile time (not map[string]interface{})
14+
- ✅ **Built-in validation** — 20+ rules in struct tags (`min`, `max`, `required`, `oneof`, `email`, `uuid`, ...), custom validators
15+
- ✅ **Secret redaction** — Mark fields `secret:"true"`, automatically redacted in errors and logs
16+
- ✅ **Human-readable errors** — Clear messages showing field, source, rule, and value (redacted for secrets)
17+
- ✅ **Multiple sources** — Explicit precedence for YAML, JSON, TOML, env, flags, K8s, Vault, AWS, Consul, etcd
18+
- ✅ **Optional cloud integrations** — Cloud sources are separate modules, not bundled (keeps core light)
19+
- ✅ **Production-ready** — v1.0.0, 80%+ test coverage, API stability guarantee
820

921
## Install
1022

1123
```bash
12-
go get github.com/MimoJanra/confkit@v1.0.0
24+
go get github.com/MimoJanra/confkit@latest
1325
```
1426

1527
## Core API
@@ -18,35 +30,65 @@ go get github.com/MimoJanra/confkit@v1.0.0
1830
// Primary entry points
1931
func Load[T any](sources ...Source) (*T, error)
2032
func LoadWithOptions[T any](options ...Option) (*T, error)
33+
func LoadContext[T any](ctx context.Context, sources ...Source) (*T, error)
34+
func LoadWithOptionsContext[T any](ctx context.Context, options ...Option) (*T, error)
2135
func LoadWithWatcher[T any](filePath string, sources ...Source) (*T, *ConfigWatcher, error)
36+
func ValidateOnly[T any](ctx context.Context, options ...Option) (*T, error)
37+
func MustLoad[T any](sources ...Source) *T // panics with *ErrorReport
38+
func MustLoadContext[T any](ctx context.Context, sources ...Source) *T
2239

2340
// Error formatting
24-
func Explain(err error) string // returns human-readable string; safe to pass to log.Fatal
25-
26-
// Config introspection
27-
func ScanFields(v any) []FieldInfo
28-
func DumpConfig(cfg any, fields []FieldInfo) ([]byte, error) // JSON; secrets → "***REDACTED***"
41+
func Explain(err error) string // human-readable; safe to pass to log.Fatal
42+
43+
// Config dump (secrets redacted by default)
44+
func Dump[T any](cfg T, opts ...DumpOption) ([]byte, error) // JSON
45+
func DumpString[T any](cfg T, opts ...DumpOption) string // JSON string
46+
func DumpYAML[T any](cfg T, opts ...DumpOption) ([]byte, error)
47+
48+
// Config file discovery
49+
func FindFile(name string, dirs ...string) (string, error)
50+
func FindSource(name string, dirs ...string) Source // returns error source wrapping ErrNotFound if absent
51+
func DefaultSearchDirs(appName string) []string // ./ ./config/ /etc/<app>/ ~/.<app>/
52+
var ErrNotFound error
53+
54+
// Overlay loading (Spring Boot-style)
55+
func FromOverlay(base, env string) Source // loads base, merges config.<env>.yaml on top if present
56+
func OverlayPath(basePath, env string) string
2957
```
3058

3159
## Option constructors (used with LoadWithOptions)
3260

3361
```go
3462
func WithSource(source Source) Option
3563
func WithValidator(name string, fn func(reflect.Value) error) Option
64+
func WithModelValidator[T any](fn func(*T) error) Option
3665
func WithMiddleware(fn MiddlewareFunc) Option
66+
func WithAuditLogger(fn AuditLogger) Option
67+
func WithLoadHook(fn LoadHookFunc) Option
68+
func WithContext(ctx context.Context) Option
3769
func WithInterpolationMaxDepth(depth int) Option
3870
```
3971

72+
## Dump options
73+
74+
```go
75+
func WithDumpFormat(f DumpFormat) DumpOption // FormatJSON (default) or FormatYAML
76+
func WithDumpRedactSecrets(redact bool) DumpOption // default: true
77+
```
78+
4079
## Built-in source constructors
4180

4281
```go
4382
confkit.FromYAML(path string) Source
83+
confkit.FromYAMLOptional(path string) Source
84+
confkit.FromYAMLFiles(paths ...string) Source
4485
confkit.FromJSON(path string) Source
86+
confkit.FromJSONFiles(paths ...string) Source
4587
confkit.FromTOML(path string) Source
88+
confkit.FromTOMLFiles(paths ...string) Source
4689
confkit.FromEnv() Source
4790
confkit.FromFlags() Source
48-
confkit.FromKubernetesConfigMap(namespace, name string) Source
49-
confkit.FromKubernetesConfigMapWithPath(namespace, name, mountPath string) Source
91+
confkit.FromFlagsWithArgs(args []string) Source
5092
confkit.NewErrorSource(err error) Source // permanently-errored source; use in custom source constructors
5193
```
5294

@@ -66,25 +108,45 @@ hidden:"true" exclude from CLI help
66108

67109
## Validation rules (validate tag)
68110

111+
Rules are comma-separated: `validate:"required,min=1,max=65535"`
112+
69113
```
70114
required value must not be zero/empty
71-
min=N int/float: value >= N; string: len >= N
72-
max=N int/float: value <= N; string: len <= N
115+
min=N int/float: value >= N; string: len >= N chars
116+
max=N int/float: value <= N; string: len <= N chars
73117
oneof=a b c string must equal one of the space-separated options
118+
email valid email address (regex-based)
119+
url valid URL (any scheme, must have host)
120+
http_url valid HTTP or HTTPS URL
121+
ip valid IP address (v4 or v6)
122+
ipv4 valid IPv4 address
123+
ipv6 valid IPv6 address
124+
uuid valid UUID (versions 1–5)
125+
hostname valid hostname (RFC 1123, max 253 chars)
126+
port valid port number (1–65535); works on int and string fields
127+
regex=PATTERN string matches the given regular expression
128+
len=N string: exactly N characters
129+
contains=S string contains substring S
130+
startswith=S string starts with prefix S
131+
endswith=S string ends with suffix S
132+
alpha string contains only Unicode letters
133+
alphanum string contains only Unicode letters and digits
134+
numeric string contains only Unicode digits
135+
lowercase string is all lowercase
136+
uppercase string is all uppercase
137+
notempty string is not blank (after TrimSpace)
74138
<custom> name of a validator registered with WithValidator
75139
```
76140

77-
Rules are comma-separated: `validate:"required,min=1,max=65535"`
78-
79141
## Source interface (implement to add custom backends)
80142

81143
```go
82144
type Source interface {
83145
Name() string
84146
Lookup(ctx context.Context, field *FieldInfo) (any, bool, error)
85-
// ("", false, nil) → not found in this source
86-
// (value, true, nil) → found
87-
// ("", false, err) → error reading source
147+
// (nil, false, nil) → not found in this source
148+
// (value, true, nil) → found
149+
// (nil, false, err) → error reading source
88150
}
89151
```
90152

@@ -103,9 +165,19 @@ type FieldInfo struct {
103165

104166
## Supported field types
105167

106-
string, int/int8/int16/int32/int64, uint/uint8/uint16/uint32/uint64,
107-
float32/float64, bool (true/false/1/0/yes/no), time.Duration ("5s","1m30s"),
108-
[]string (comma-separated), []int (comma-separated)
168+
```
169+
string
170+
int / int8 / int16 / int32 / int64
171+
uint / uint8 / uint16 / uint32 / uint64
172+
float32 / float64
173+
bool — true / false / 1 / 0 / yes / no
174+
time.Duration — "5s", "1m30s", "2h"
175+
time.Time — RFC3339: "2006-01-02T15:04:05Z07:00"
176+
[]string — comma-separated: "a,b,c"
177+
[]int — comma-separated: "1,2,3"
178+
map[string]string — KEY=val,KEY2=val2 (quoted: KEY="val,with,commas")
179+
map[string]int — KEY=1,KEY2=2
180+
```
109181

110182
## Minimal working example
111183

@@ -169,7 +241,17 @@ cfg, err := confkit.LoadWithOptions[Config](
169241
```go
170242
cfg, watcher, err := confkit.LoadWithWatcher[Config]("config.yaml",
171243
confkit.FromYAML("config.yaml"), confkit.FromEnv())
244+
245+
// Basic listener
172246
watcher.AddListener(func(old, new any, err error) { /* react to change */ })
247+
248+
// Delta listener — see exactly which fields changed
249+
watcher.AddDeltaListener(func(delta confkit.ConfigDelta, old, new map[string]any, err error) {
250+
for _, k := range delta.Changed { log.Printf("changed: %s", k) }
251+
for _, k := range delta.Added { log.Printf("added: %s", k) }
252+
for _, k := range delta.Removed { log.Printf("removed: %s", k) }
253+
})
254+
173255
watcher.SetPollInterval(5 * time.Second)
174256
watcher.Start()
175257
defer watcher.Stop()
@@ -178,23 +260,27 @@ defer watcher.Stop()
178260
## Enterprise sources (separate modules, import only what you need)
179261

180262
```go
181-
// go get confkit/vault
263+
// go get github.com/MimoJanra/confkit/k8s
264+
k8s.FromKubernetesConfigMap(namespace, name string) confkit.Source
265+
k8s.FromKubernetesConfigMapWithPath(namespace, name, mountPath string) confkit.Source
266+
267+
// go get github.com/MimoJanra/confkit/vault
182268
vault.FromVault(addr string, auth vault.VaultAuth, pathPrefix string) confkit.Source
183269
vault.FromVaultWithKVVersion(addr string, auth vault.VaultAuth, kvVersion int, pathPrefix string) confkit.Source
184270
vault.VaultTokenAuth(token string) vault.VaultAuth
185271
vault.VaultAppRoleAuth(roleID, secretID string) vault.VaultAuth
186272
vault.VaultKubernetesAuth(role, jwt string) vault.VaultAuth
187273

188-
// go get confkit/consul
274+
// go get github.com/MimoJanra/confkit/consul
189275
consul.FromConsul(addr string) confkit.Source
190276
consul.FromConsulWithToken(addr, token string) confkit.Source
191277
consul.FromConsulWithOptions(addr, token, datacenter string) confkit.Source
192278

193-
// go get confkit/etcd
279+
// go get github.com/MimoJanra/confkit/etcd
194280
etcd.FromEtcd(endpoints []string) confkit.Source
195281
etcd.FromEtcdWithPrefix(endpoints []string, prefix string) confkit.Source
196282

197-
// go get confkit/aws
283+
// go get github.com/MimoJanra/confkit/aws
198284
aws.FromAWSSSMParameterStore(pathPrefix string) confkit.Source
199285
aws.FromAWSSSMParameterStoreWithTTL(pathPrefix string, ttl time.Duration) confkit.Source
200286
aws.FromAWSSecretsManager(secretName string) confkit.Source
@@ -207,7 +293,7 @@ aws.FromAWSSSMParameterStoreMultiRegion(pathPrefix string, regions []string) con
207293
## Schema generation
208294

209295
```go
210-
import "confkit/schema"
296+
import "github.com/MimoJanra/confkit/schema"
211297
s, _ := schema.GenerateSchema[Config]() // *schema.Schema (JSON Schema draft-07)
212298
schema.GenerateMarkdown[Config]() // Markdown reference table
213299
schema.GenerateCLIHelp[Config]() // --help style text
@@ -221,6 +307,9 @@ Sources passed to Load are applied left to right; later sources override earlier
221307

222308
```go
223309
type ErrorReport struct{ Errors []FieldError } // implements error
310+
func (r *ErrorReport) FirstError() *FieldError // nil if no errors
311+
func (r *ErrorReport) Unwrap() []error
312+
224313
type FieldError struct {
225314
Path string // dot-path: "Database.Password"
226315
Source string // "env" | "yaml" | "validation" | "default"
@@ -236,6 +325,7 @@ type FieldError struct {
236325

237326
```
238327
confkit — core (yaml.v3 + go-toml/v2 only)
328+
confkit/k8s — Kubernetes ConfigMap/Secret source
239329
confkit/vault — HashiCorp Vault KV source
240330
confkit/consul — HashiCorp Consul KV source
241331
confkit/etcd — etcd v3 source

0 commit comments

Comments
 (0)