Install
$ agentstack add skill-xobotyi-cc-foundry-zog ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Zog
Zod-inspired schema validation for Go. Declarative schema builder with runtime parsing and validation. Import as z "github.com/Oudwins/zog".
- All fields are optional by default (opposite of Zod)
- Schema keys in
z.Shape{}must match struct field names, not input data keys — use struct tags (json,form,
zog) for input key mapping
- All schemas return
ZogIssueList - Check errors with
len(errs) > 0, noterrs != nil - Zog panics on schema misconfiguration (destination type mismatch, missing struct fields) but never on invalid
input data
- Do not depend on test execution order — tests may run in parallel in future versions
Parse vs Validate
Two entry points, same schemas:
schema.Parse(data, &dest, ...opts)— coerces untyped input into destination. Use at IO boundaries (HTTP, JSON, env)schema.Validate(&value, ...opts)— validates existing Go values. No coercion. More efficient when data is already
typed
Key difference: Validate treats zero values as missing when Required() is set. Use z.Ptr() + .NotNil() to distinguish "not provided" from "zero value":
// Parse can distinguish 0 from missing
z.Int().Required().Parse(0, &dest) // ok
// Validate cannot — use pointer
z.Ptr(z.Int()).NotNil().Validate(&valPtr) // nil = missing, *0 = valid
Prefer Validate when data is already typed. Use Parse when accepting external input that needs coercion.
Schema Types
Primitives
All primitive .Parse() / .Validate() return ZogIssueList.
String:
z.String()— base string schema.Trim()— transform: trims whitespace.Min(n),.Max(n),.Len(n)— length validators.Email(),.URL(),.UUID(),.IPv4()— format validators.Match(regex)— regex match.Contains(s),.ContainsUpper(),.ContainsDigit(),.ContainsSpecial()— content validators.HasPrefix(s),.HasSuffix(s)— prefix/suffix validators.OneOf([]string{...})— enum-like validation (replaces Zod'sz.Enum()).Not()— negates the next test
Numbers:
z.Int(),z.Int32(),z.Int64(),z.Float32(),z.Float64()— numeric schemas.GT(n),.GTE(n),.LT(n),.LTE(n),.EQ(n)— comparison validators.OneOf([]T{...})— enum-like validation.Not()— negates the next test
Bool:
z.Bool()— boolean schema.True(),.False(),.EQ(v)— value validators
Time:
z.Time()— validatestime.Time.After(t),.Before(t),.Is(t)— temporal validatorsz.Time(z.Time.Format(layout))— parse strings using layout (default:time.RFC3339). Coercion only works with
Parse(), not Validate()
Complex Types
Struct:
schema := z.Struct(z.Shape{
"name": z.String().Required(),
"age": z.Int().GT(0),
})
.Pick("key1", map[string]bool{"a": true})— shallow copy with only specified fields.Omit("key1", map[string]bool{"a": true})— shallow copy without specified fields.Extend(z.Shape{...})— shallow copy with additional fields.Merge(other1, other2)— merge schemas, last wins on conflict- Structs cannot be
Required()orOptional()— usez.Ptr(z.Struct(...))for optional structs
Slice:
schema := z.Slice(z.String().Required())
.Min(n),.Max(n),.Length(n)— size validators.Contains(val)— element presence validator.Not()— negates the next test
Pointer:
z.Ptr(z.String()) // pointer to string
.NotNil()— equivalent toRequired()for other types
Boxed:
z.Boxed[BoxType, InnerType](innerSchema, unboxFunc, boxFunc)
Wraps a schema with custom box/unbox logic for types like sql.NullString, driver.Valuer, or custom wrappers. The unboxFunc extracts the inner value; boxFunc creates the wrapper from validated value (can be nil if boxing not needed).
Custom Primitive Schemas
For named types based on primitives:
type Env string
z.StringLike[Env]().OneOf([]Env{"prod", "dev"})
Available: z.StringLike[T](), z.IntLike[T](), z.FloatLike[T](), z.UintLike[T](), z.BoolLike[T]()
CustomFunc
Quick validation for non-primitive types without defining a full schema:
z.CustomFunc(func(valPtr *uuid.UUID, ctx z.Ctx) bool {
return valPtr.IsValid()
}, z.Message("invalid uuid"))
Does not support coercion.
Generic Schema Methods
Available on all schema types:
.Required()— field must be present and non-zero.Optional()— field can be absent (default behavior).Default(val)/.DefaultFunc(fn)— use this value when input is zero. Takes priority overRequired(). Tests
still run
.Catch(val)/.CatchFunc(fn)— on any error, set destination to this value and stop execution.Test(z.Test{...})— complex custom test (like Zod'ssuperRefine).TestFunc(fn, ...opts)— simple custom test (like Zod'srefine).Transform(func(valPtr *T, ctx z.Ctx) error)— post-validation in-place mutation
Execution order: nil check → default/required → coerce → validation loop (tests + transforms in order) → catch on error
Struct Tags
Schema keys match struct field names by default. Use struct tags for input key mapping:
json— JSON inputform— form dataquery— query paramsenv— environment variableszog— catch-all, works for any input
Priority: json/form/query/env → zog → schema field name
type User struct {
Name string `zog:"first-name"`
LastName string `query:"last_name" json:"last-name"`
}
Custom Tests
Simple (refine-like): Return bool — Zog creates the issue:
z.String().TestFunc(func(data *string, ctx z.Ctx) bool {
return *data == "expected"
}, z.Message("must be expected"))
For structs/slices, the parameter is any — cast manually:
z.Struct(z.Shape{...}).TestFunc(func(dataPtr any, ctx z.Ctx) bool {
user := dataPtr.(*User)
return user.Name != ""
})
Complex (superRefine-like): Add issues manually via context:
z.String().Test(z.Test{
Func: func(val any, ctx z.Ctx) {
s := val.(string)
if !isValid(s) {
ctx.AddIssue(ctx.Issue().SetMessage("invalid value"))
}
},
})
Reusable tests: Wrap in functions returning z.Test[T]:
func MinWords(n int, opts ...z.TestOption) z.Test[any] {
options := []z.TestOption{z.Message(fmt.Sprintf("must have at least %d words", n))}
options = append(options, opts...)
return z.TestFunc(func(val any, ctx z.Ctx) bool {
s := val.(*string)
return len(strings.Fields(*s)) >= n
}, options...)
}
Transforms
Post-validation mutation via pointer. Runs in declaration order with tests:
z.String().Min(3).Transform(func(valPtr *string, ctx z.Ctx) error {
*valPtr = strings.ToLower(*valPtr)
return nil
})
Struct transforms receive any — cast to struct pointer:
z.Struct(z.Shape{...}).Transform(func(dataPtr any, ctx z.Ctx) error {
user := dataPtr.(*User)
user.FullName = user.First + " " + user.Last
return nil
})
Returning an error from a transform stops execution and produces an issue.
Preprocess
Transform input before parsing. Pure function — creates a copy:
z.Preprocess(func(data any, ctx z.Ctx) (any, error) {
s, ok := data.(string)
if !ok {
return nil, fmt.Errorf("expected string, got %T", data)
}
return strings.Split(s, ","), nil
}, z.Slice(z.String().Email().Required()))
Footgun: With Validate(), the data argument is a pointer to the value, not the raw input.
Error Handling
ZogIssue
type ZogIssue struct {
Code zconst.ZogIssueCode // issue identifier (e.g., "required", "min", "email")
Path []string // location in data structure (nil for root primitives)
Value any // input value that caused the issue
Dtype string // destination type
Params map[string]any // test parameters (may be nil)
Message string // human-readable, user-safe message
Err error // underlying error or nil
}
All schemas return ZogIssueList = []*ZogIssue. Each issue has a Path field:
- Primitive root:
nil - Struct field:
[]string{"name"} - Nested:
[]string{"address", "streets", "[0]"} - Path to string:
issue.PathString()orz.Issues.FlattenPath(issue.Path)
Formatting Strategies
z.Issues.Flatten(errs)—map[path][]messages. Root issues under"$root"key (zconst.ISSUE_KEY_ROOT)z.Issues.Treeify(errs)— nested tree mirroring data structure. Each node haserrorsarray andproperties/itemsz.Issues.Prettify(errs)— human-readable string with✖prefix and→ at pathlines
Issue Codes
Common codes: required, coerce, custom, min, max, len, email, uuid, url, match, gt, gte, lt, lte, eq, one_of_options, after, before, true, false, prefix, suffix, contains_upper, contains_digit, contains_special, contained
HTTP-specific: invalid_json, invalid_form, invalid_query
Custom Messages
Per-test: z.Message("text"), z.MessageFunc(fn), z.IssueCode("code"), z.IssuePath("path")
z.String().Min(5, z.Message("must be at least 5 chars"))
Per-execution: z.WithIssueFormatter(fn) — overrides all messages for one Parse/Validate call
Global: Override conf.DefaultIssueMessageMap[zconst.TypeString][zconst.IssueCodeRequired] or replace conf.IssueFormatter
Test Options
Options passed to any test:
z.Message("...")— static issue messagez.MessageFunc(func(e *ZogIssue, ctx z.Ctx))— dynamic issue messagez.IssueCode("...")— override issue codez.IssuePath("...")— override issue path
Execution Options
Options passed to Parse() / Validate():
z.WithCoercer(fn)— per-schema coercion override (Parse only)z.WithIssueFormatter(fn)— override message formatting for this executionz.WithCtxValue(key, val)— pass custom data to tests/transforms viactx.Get(key)
Context
z.Ctx interface available in tests, transforms, and preprocessors:
ctx.Get(key)— retrieve value set viaz.WithCtxValue()ctx.AddIssue(issue)— add a custom issuectx.Issue()— create a new issue pre-filled with current schema context (path, value, type)
HTTP Integration (zhttp)
import "github.com/Oudwins/zog/zhttp"
errs := userSchema.Parse(zhttp.Request(r), &user)
zhttp.Request(r) auto-detects content type (JSON, form, query params). Only supports parsing into structs.
Invalid input produces a root issue with code invalid_json, invalid_form, or invalid_query — the schema does not run.
JSON Integration (zjson)
import "github.com/Oudwins/zog/parsers/zjson"
errs := userSchema.Parse(zjson.Decode(bytes.NewReader(jsonBytes)), &user)
Accepts io.Reader or io.ReaderCloser. Only supports structs.
Environment Variables (zenv)
import "github.com/Oudwins/zog/zenv"
errs := envSchema.Parse(zenv.NewDataProvider(), &cfg)
Use env or zog struct tags for key mapping. Coercion handles string-to-type conversion automatically.
i18n
import (
"github.com/Oudwins/zog/i18n"
"github.com/Oudwins/zog/i18n/en"
"github.com/Oudwins/zog/i18n/es"
)
// Setup
i18n.SetLanguagesErrsMap(map[string]i18n.LangMap{
"en": en.Map,
"es": es.Map,
}, "en") // default language
// Per-execution
schema.Parse(data, &dest, z.WithCtxValue(i18n.LangKey, "es"))
Single language: set conf.DefaultErrMsgMap = es.Map.
Global Configuration
Via github.com/Oudwins/zog/conf:
conf.Coercers.Float64 = func(data any) (any, error) {...}— override type coercionconf.DefaultCoercers.Float64(data)— fallback to default coercerconf.DefaultIssueMessageMap— override issue messages per type and codeconf.IssueFormatter— replace the global issue formatter
Patterns
Struct Validate Method
var userSchema = z.Struct(z.Shape{
"ID": z.Int().Required(),
"Name": z.String().Required().Min(2),
})
func (u *User) Validate() z.ZogIssueList {
return userSchema.Validate(u)
}
HTTP Handler
func handleCreate(w http.ResponseWriter, r *http.Request) {
var user User
if errs := userSchema.Parse(zhttp.Request(r), &user); len(errs) > 0 {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(z.Issues.Flatten(errs))
return
}
// user is valid and populated
}
Environment Config
var envSchema = z.Struct(z.Shape{
"PORT": z.Int().GT(1000).LT(65535).Default(3000),
"DB_HOST": z.String().Default("localhost"),
})
var Env = func() Config {
var c Config
if errs := envSchema.Parse(zenv.NewDataProvider(), &c); len(errs) > 0 {
log.Fatal(z.Issues.Prettify(errs))
}
return c
}()
Limitations
- No
z.Map()schema zhttpandzjsononly parse into structs- Structs/slices do not support
Catch(); structs do not supportDefault() - Deeply nested schemas incur reflection overhead
Pick,Omit,Extend,Mergeare not type-safe
Application
When writing code with Zog: apply all conventions silently — don't narrate each rule. Always check errors with len(errs) > 0. Define schemas as package-level var, not inside functions. If an existing codebase contradicts a convention, follow the codebase and flag the divergence once.
When reviewing code with Zog: cite the specific violation and show the fix inline. Proactively flag: errs != nil checks (must be len(errs) > 0), missing Required() on mandatory fields, schema keys not matching struct field names, and missing struct tags for external input mapping.
Integration
The golang skill governs Go language conventions. This skill governs Zog-specific API and patterns. When both apply, Zog idioms (e.g., z.Shape{} key naming) take precedence over general Go naming rules within schema definitions.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: xobotyi
- Source: xobotyi/cc-foundry
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.