neuron-explorer
Qwen3.5‑4B 전용 조향형 뉴런 탐색기, 모든 MLP 뉴런을 밀어 결과를 보관·탐색
docuconf는 앱이 선언한 설정을 타입이 지정된 계약으로 바꾸어 쿠버네티스가 배포 전 검증하고, 앱도 부팅 시 재검증합니다
docuconf turns the config your app already declares into a typed contract that your Kubernetes platform checks before anything deploys, and your app checks again at boot.
개발 도구Go · CUEGitHub @Docuconf온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗The Go SDK for docuconf: typed configuration contracts between an app and the Kubernetes platform that runs it. Your config struct stays a normal caarlos0/env struct. docuconf adds descriptions, secrets, constraints and file inputs, checks everything at boot, and exports a CUE contract the platform validates before it deploys.
Documentation: docuconf.dev · Go guide
The CLI is also released as binaries for Linux, macOS and Windows (with SHA256SUMS) on the
GitHub Releases page, and as a multi-arch image,
ghcr.io/docuconf/docuconf, holding a single static binary at /docuconf. To add it to your own image:
COPY --from=ghcr.io/docuconf/docuconf:<v> /docuconf /usr/local/bin/docuconf
See RELEASING.md for the tags and everything that is published.
Status: early, spec
v1alpha1. Expect breaking changes until v1.
Every Go snippet and YAML file below is taken from examples/orders, which CI builds, tests, exports and smoke-tests on every push.
go get github.com/docuconf/docuconf-go@main
go get -tool github.com/docuconf/docuconf-go/cmd/docuconf-export@main
The first line adds the SDK (Go 1.24 or later). The second pins the exporter as a Go tool. It needs only the SDK, so it adds nothing else to your go.mod.
No version is tagged yet, so @main resolves to a pseudo-version. go get github.com/docuconf/[email protected] will work from the first release. To build against a local checkout instead:
go mod edit -replace github.com/docuconf/docuconf-go=../docuconf-go
The platform side (vet, render, helm) and the docs generator (docs) are the docuconf CLI. It needs Go 1.25 because of CUE: go install github.com/docuconf/docuconf-go/cmd/docuconf@main.
Put the struct in its own package, usually internal/config, because the exporter imports it:
package config
import (
"time"
"github.com/docuconf/docuconf-go"
)
// Config is everything the orders service reads at boot.
// Doc comments become the descriptions in the contract: the first
// paragraph is the description, and any later paragraphs are its details.
type Config struct {
// HTTP listen port.
Port int `env:"PORT" envDefault:"8080" min:"1" max:"65535"`
// Minimum log level emitted.
LogLevel string `env:"LOG_LEVEL" envDefault:"info" values:"debug,info,warn,error"`
// Postgres connection string for the orders database.
DatabaseURL docuconf.Secret `env:"DATABASE_URL,required" schemes:"postgres" maxLength:"2048"`
// Origins allowed to call the API from a browser.
AllowedOrigins []string `env:"ALLOWED_ORIGINS" envDefault:"http://localhost:3000" minItems:"1"`
// Time limit for handling one request.
RequestTimeout time.Duration `env:"REQUEST_TIMEOUT" envDefault:"30s" min:"1s" max:"5m"`
// Number of background workers processing orders.
//
// Each worker holds one database connection, so keep it below the
// database's connection limit divided by the number of replicas.
// Raise it when the order queue grows faster than it drains.
WorkerCount int `env:"WORKER_COUNT" envDefault:"4" min:"1" max:"64"`
// Keys that verify the signature on incoming payment webhooks.
//
// A webhook is accepted when it is signed with any key in the set, so
// the key can be rotated without turning webhooks away. Each key is 32
// to 256 characters, so an empty or truncated key fails at boot.
// Without this variable, the service rejects every webhook.
WebhookKeys docuconf.KeySet `env:"WEBHOOK_KEYS" keyMinLength:"32" keyMaxLength:"256"`
}
Every input needs a description: the first paragraph of the field's doc comment, or a desc tag. Later paragraphs become the input's optional details, Markdown that says why the input exists and when to change it. Headings (# Heading), lists and indented code blocks in the comment carry over as Markdown. Details only go into generated docs; nothing reads them at runtime.
docuconf.Secret is a string that prints *** everywhere: %v, %+v, slog and JSON. A field of that type is secret in the contract. On a plain string, secret:"true" does the same for the contract but not for printing. docuconf.KeySet, such as the webhook keys above, is a set of secret keys that are all valid at once, which is how a key is rotated without downtime (spec section 6.1): it holds one to two keys unless minKeys and maxKeys say otherwise, prints *** like a Secret, and its Contains and Verify methods check a candidate against every key. The generated docs print the rotation steps. A misspelled tag (secrte, mni) is an error, not a silently dropped rule.
func main() {
// On bad configuration, ParseOrExit prints every problem and exits 1.
cfg := docuconf.ParseOrExit[config.Config]()
slog.Info("config loaded", "config", docuconf.LogValue(cfg)) // secrets print as ***
Use cfg.DatabaseURL.Reveal() where you need the secret's value. docuconf.Redacted(cfg) returns the configuration as a map with secrets as ***, ready for a debug endpoint:
mux.HandleFunc("GET /config", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(docuconf.Redacted(cfg))
})
DATABASE_URL=postgres://orders:pw@localhost:5432/orders go run .
With PORT=0 and no DATABASE_URL, the service lists every problem, not only the first, and exits 1:
$ PORT=0 DATABSE_URL=x go run .
2026/10/07 13:30:43 WARN docuconf: DATABSE_URL is set but not declared; did you mean DATABASE_URL?
docuconf: 2 configuration problems:
PORT: 0 is below min 1 (out_of_range)
DATABASE_URL: is required but not set (missing_required)
Each line names the variable and a stable code (missing_required, out_of_range, certificate_expiring, ...). The same lines go to /dev/termination-log, so kubectl describe pod shows them. Secret values never appear in a message, including messages from your own TextUnmarshaler or FuncMap parsers. A set variable that looks like a typo of a declared one gets a warning, as DATABSE_URL does above.
When you want the error yourself instead of exiting, call docuconf.Parse[config.Config](). It returns the zero Config and a *docuconf.ValidationError with every violation.
ParseWithOptions loads from a map. It never reads or changes the process environment, and it starts nothing you have to stop:
func TestConfig(t *testing.T) {
_, err := docuconf.ParseWithOptions[config.Config](docuconf.Options{
// Read these variables instead of the process environment.
Environment: map[string]string{"DATABASE_URL": "postgres://u:p@db/orders", "PORT": "0"},
TerminationLog: "-", // do not write /dev/termination-log
})
var verr *docuconf.ValidationError
if !errors.As(err, &verr) || !verr.Has(docuconf.CodeOutOfRange) {
t.Fatalf("want out_of_range for PORT=0, got %v", err)
}
}
Options.FileRoot points file inputs at a test directory, and Options.Now fixes the clock for certificate checks.
go tool docuconf-export -pkg ./internal/config -name orders-api -package orders -o contract.cue
Commit contract.cue and check in CI that it is up to date. -check exits 1 and prints a diff when it is stale:
go tool docuconf-export -pkg ./internal/config -name orders-api -package orders -check contract.cue
-type defaults to Config, and -name (the service name, a DNS label) to the last element of your module path. Export compiles and runs a tiny program inside your module (with go run), so it needs the Go toolchain and your module's dependencies, but no environment values. The init functions of your config package and everything it imports run during export, so keep that package free of side-effecting imports. From Go code, docuconf.Export[config.Config](docuconf.Meta{Name: "orders-api"}) returns the same contract.
If the app parses with a Prefix or a FuncMap, declare them on the struct so the app and the exporter cannot disagree:
func (Config) DocuconfOptions() docuconf.Options {
return docuconf.Options{Prefix: "ORDERS_", FuncMap: parsers}
}
docuconf docs turns the contract into documentation, so the docs cannot drift from the code either. Commit the output next to contract.cue:
docuconf docs contract.cue -o CONFIG.md
docuconf docs contract.cue --format agents -o CONFIG.agents.md
CONFIG.md is the reference for developers: a table of contents by group, then each input's type, default, constraints, wire format, allowed sources, examples and details, the file inputs, and what each boot error means.CONFIG.agents.md is for AI agents, both coding agents in the app's repository and agents that set deployment values. It starts with hard rules (never write a secret's value anywhere, use each input's wire format, run docuconf vet before proposing a change, invent no inputs), then gives one block of key: value facts per input. Include it from your AGENTS.md, or serve it as an llms.txt.--format model writes docs.json, the docs model both renderers read (spec section 14). A website, an MCP server or a Backstage plugin can render from it, and docuconf docs docs.json renders it like a contract.Like the contract, CI can check that the committed docs are current. --check exits 1 and prints a diff when a file is stale:
docuconf docs contract.cue --check CONFIG.md
docuconf docs contract.cue --format agents --check CONFIG.agents.md
The platform validates its inputs against contract.cue before anything reaches the cluster. It writes a values file, with secrets as references:
# values.yaml: what the platform sets each variable to.
LOG_LEVEL: warn
ALLOWED_ORIGINS: [https://shop.example.com]
WORKER_COUNT: 8
DATABASE_URL: # a secret: always a reference, never a literal
secretKeyRef: {name: orders-db, key: url}
WEBHOOK_KEYS: # a key set: one Secret key holding "old,new" while rotating
secretKeyRef: {name: orders-webhooks, key: keys}
and, when the app has file inputs, a files file:
# files.yaml: where each file input comes from.
serving-tls:
certificate: # a cert-manager Certificate
name: orders-tls
secretName: orders-tls
dnsNames: [orders.example.com]
duration: 2160h
renewBefore: 720h
discounts:
inline:
codes: {WELCOME10: 10, SPRING25: 25}
docuconf vet -contract contract.cue -values deploy/values.yaml -files deploy/files.yaml
docuconf render -contract contract.cue -values deploy/values.yaml -files deploy/files.yaml
vet prints one line per problem, such as PORT: 70000 is above max 65535 or LOG_LEVL: is not declared in the contract (check the spelling), and exits 1. -policy prod.cue adds an environment's own rules. render turns valid inputs into the pod's env, volumes and ConfigMaps. A Helm-based platform can use the docuconf Helm chart, and a Crossplane composition can evaluate the contract in plain CUE. The walkthrough shows all three paths end to end, including a policy file.
A field of a docuconf file type is a file input, mounted by the platform:
// Certificate to serve HTTPS with. Without it, the service serves HTTP.
TLS docuconf.TLSKeyPair `file:"serving-tls" path:"/etc/orders/tls" dnsNames:"orders.example.com" minRemaining:"720h" reload:"watch"`
// Discount codes accepted at checkout.
Discounts docuconf.ConfigFile[Discounts] `file:"discounts" path:"/etc/orders/discounts/discounts.yaml"`
}
// Discounts is the content of the discounts file.
type Discounts struct {
// Percent off for each discount code.
Codes map[string]int `json:"codes" yaml:"codes"`
}
if cfg.TLS.Present() {
srv.TLSConfig = &tls.Config{GetCertificate: cfg.TLS.GetCertificate}
File types: TLSKeyPair, CABundle, Keystore (PKCS#12), TextFile, BinaryFile and ConfigFile[T] (JSON or YAML, with a JSON Schema generated from T). In T, a field without omitempty (or omitzero) that is not a pointer is required in the file, unlike plain Go decoding, which would leave it at its zero value. Add omitempty to make a field optional.
To run locally with files, DOCUCONF_FILE_ROOT remaps every path under a directory: /etc/orders/tls is read from ./dev/etc/orders/tls. This makes a certificate that satisfies the declaration:
mkdir -p dev/etc/orders/tls dev/etc/orders/discounts
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes -days 90 \
-subj /CN=orders.example.com -addext subjectAltName=DNS:orders.example.com \
-keyout dev/etc/orders/tls/tls.key -out dev/etc/orders/tls/tls.crt 2>/dev/null
echo 'codes: {WELCOME10: 10}' > dev/etc/orders/discounts/discounts.yaml
Then run with DOCUCONF_FILE_ROOT=./dev. .env files are read only when listed in Options.DotEnv. Set Options.DotEnvRequired to make a missing one an error.
With reload:"watch", docuconf swaps in a changed file once it passes the checks it passed at boot; a change that fails them is logged and not used, and the previous value stays current. Changes are found by each file's identity, size and modification time, following symlinks, so the kubelet's swap of a projected volume is seen. Files are checked at most once per Options.WatchInterval (10s by default): when the app reads the value, and in the background while an on-change hook is registered.
An app that copies a watched value once, into a TLS server context, an HTTP client or a pool, keeps the old value until the certificate expires. Read the value on each use, or rebuild what you made from it in an OnChange hook. A TLS server reads the certificate on every handshake with GetCertificate; the hook here only logs:
if cfg.TLS.Present() {
// GetCertificate reads the current certificate on every handshake,
// so a renewed one is served without a restart.
srv.TLSConfig = &tls.Config{GetCertificate: cfg.TLS.GetCertificate}
cfg.TLS.OnChange(func(cert *tls.Certificate) {
slog.Info("serving a renewed certificate", "notAfter", cert.Leaf.NotAfter)
})
An HTTP client copies its CA pool when it is built, so build a new one when a CABundle changes:
build := func(certs []*x509.Certificate) {
pool := x509.NewCertPool()
for _, cert := range certs {
pool.AddCert(cert)
}
next := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{RootCAs: pool}}}
if old := c.cur.Swap(next); old != nil {
old.CloseIdleConnections()
}
}
build(ca.Certificates())
c.cancel = ca.OnChange(build)
TLSKeyPair, CABundle, Keystore, TextFile and ConfigFile[T] each have OnChange(fn), which returns a function that unregisters fn. A hook is called with the new value after a reload is accepted, never after a rejected one. Hooks run one at a time; one that panics is logged by input name and panic type, and the others still run. BinaryFile has no hook: the app opens the file itself, so it always reads the current one.
ReloadStatus() returns the input's Generation (1 after boot, plus one per accepted reload), LastReload (the time of the last accepted reload) and LastRejected (the last rejected change: its time, input and violation codes, never the content; cleared by the next accepted reload). It encodes as JSON for a health check:
mux.HandleFunc("GET /reloadz", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]docuconf.ReloadStatus{"serving-tls": cfg.TLS.ReloadStatus()})
})
A Keystore reload opens the new keystore with the password read at boot, since a process's environment does not change. Rotating a keystore's password needs a rollout; a changed keystore that does not open with the boot password is rejected as keystore_unreadable, and the previous one stays current.
A doc comment's first paragraph is the description (a desc tag is the fallback), and the rest is details. docuconf's tags: secret, min/max, minLength/maxLength (in characters; maxLength also bounds a url or a JSON[T] value), pattern (RE2), values (enum), schemes (url), minItems/maxItems, itemMin/itemMax on integer lists, itemMinLength/itemMaxLength on string lists (Shards []int `env:"SHARDS" itemMin:"0" itemMax:"1023"`), minKeys/maxKeys and keyMinLength/keyMaxLength on a KeySet, and deprecated (OldPort int `env:"OLD_PORT" deprecated:"Use PORT"`), which marks an input for removal: docuconf vet warns while the platform still sets it, and the SDK logs a warning at boot, never with the value. A required input cannot be deprecated. Integer bounds always include the range caarlos0/env parses the Go type with: an int exports min: -2147483648, max: 2147483647 because caarlos0/env parses it as 32 bits, and a []uint16 exports itemMin: 0, itemMax: 65535. JSON[T] holds a structured variable. The full tag reference is in the package docs.
A comma-separated list keeps empty items, as caarlos0/env does: ALLOWED_ORIGINS="," is two empty strings and satisfies minItems:"1". The contract accepts empty items too, so vet and boot agree. Check for empty items in your code if they matter.
Secrets injected at startup, by Bank-Vaults' vault-env, a wrapper such as op run, or vals, need nothing special: docuconf reads the environment as it is when the process starts, after injection, and validates the real values. If a secret variable still holds a reference (it starts with vault:, op:// or ref+), the injector did not run, and loading fails with invalid_type, naming the variable and the reference scheme but never the value.
caarlos0/env reads only environment variables and does not layer config files, so the Go SDK has no config-file overlays (spec section 4.7) and never exports overlays. A platform supplies every Go variable through the environment.
To validate an environment against a contract with no Go struct, for example one written in CUE by hand and converted with cue export --out json, use LoadContract. It parses every wire encoding (lists as csv, json or indexed; durations as go, iso8601, seconds or timespan) and returns typed values, or a *ValidationError with every violation:
vals, err := docuconf.LoadContract(contractJSON, docuconf.Options{})
if err != nil {
return 0, err // a *docuconf.ValidationError with every violation
}
timeout := vals["REQUEST_TIMEOUT"].(time.Duration) // int is int64, list is []string or []int64
File inputs are loaded too, with the same checks as the Go file types, and returned by input name as those types (TLSKeyPair, CABundle, Keystore, TextFile, BinaryFile, or ConfigFile[any] checked against the contract's schema). Config files may be json, yaml or toml; a jks keystore is rejected, since the Go SDK reads pkcs12 only. A watched file input reloads exactly as in a struct, with OnChange and ReloadStatus. A contract's profiles and overlays are layered as a host with config files would (SPEC §4.4, §4.7): a variable's default, then the selected profile's default, then an overlay (read from under DOCUCONF_FILE_ROOT), then the environment. LoadContract returns values read once, so an overlay declared reload: watch is a *DeclarationError; docuconf check and exec check such an overlay once, at boot.
A generator for another language that builds the contract itself can format it with docuconf.ContractCUE(contractJSON, pkg), which checks it as LoadContract does and writes the same contract.cue layout as Export: header, package, import, variables and files sorted by name. The COBOL SDK's docuconf-cobol generate uses it.
docuconf exec and docuconf checkA program written in a language without a docuconf SDK (a shell script, a COBOL batch job, a vendor binary) can still be checked at boot against its contract. docuconf exec validates the environment and file inputs with the Go SDK's contract-first loader (LoadContract, which passes the whole conformance suite), then replaces itself with the program:
COPY --from=docuconf /docuconf /usr/local/bin/docuconf
COPY contract.cue /etc/docuconf/contract.cue
ENTRYPOINT ["docuconf", "exec", "-contract", "/etc/docuconf/contract.cue", "--", "/app/orders"]
On success it execs the program (execve): the program gets docuconf's PID, so it is PID 1 in the container and receives SIGTERM directly. docuconf prints nothing.
The program gets exactly the environment that was validated: the process environment, then any -env-file values for variables it does not set (the process environment wins), then the contract's default for every variable still unset (empty counts as unset except for string). Defaults are exported in the variable's wire encoding (a list joined by its separator, or as json or NAME__0, NAME__1; a duration as go, iso8601, seconds or timespan), and a file input whose pathEnv is unset gets its path. A shell script or vendor binary therefore never repeats the contract's defaults. -no-defaults turns this off and passes only what was set.
On failure it prints every violation on its own line, never a secret value, writes them to the termination log (DOCUCONF_TERMINATION_LOG, else /dev/termination-log when it exists), and exits 1 without starting the program:
docuconf: 2 configuration problems:
DATABASE_URL: is required but not set (missing_required)
PORT: 0 is below min 1 (out_of_range)
File paths honour each input's pathEnv and DOCUCONF_FILE_ROOT, as an SDK does.
-env-file .env (repeatable) reads a .env file for local runs. Its values are validated and passed to the program; the process environment wins over it.
Exit codes: 1 for configuration problems, 2 for a bad contract or usage, 127 when the program cannot be started.
docuconf check -contract contract.cue runs the same validation and exits 0 (printing <name>: ok) or 1, without starting anything. Use it in an init container or in CI.
The checks are the SDK's: contract-first mode loads variables, files, profiles and overlays, and it reads json, yaml and toml config files and pkcs12 keystores (not jks).
go test ./... runs the shared conformance suite (spec section 12) from conformance/cases.json through LoadContract, one subtest per case id. To run another copy of the suite, set DOCUCONF_CONFORMANCE to its cases.json:
go test -run TestConformance -v .
DOCUCONF_CONFORMANCE=/path/to/cases.json go test -run TestConformance .
The Go SDK supports every capability tag (int64, json-schema) and every transitional one (key-set, deprecated, strict-parsing, files, profiles, overlays), so no case is skipped, and the test fails if one is. Its export of the shared export fixture is kept in testdata/conformance-export.cue and compared with conformance/export/golden.cue by the CLI's tests.
spec/cuev1beta1MIT.