Install
$ agentstack add skill-jkaninda-okapi-skills-validation ✓ 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 No
- ✓ 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.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Okapi Validation
Validation is declarative: struct tags drive both runtime enforcement and OpenAPI schema generation. Validation runs automatically inside c.Bind and inside the typed handler wrappers (okapi.Handle, okapi.H, okapi.HandleIO, okapi.HandleO).
For where values come from (JSON body, query, path, header, cookie, form), see the request_binding/ skill.
Constraint Tags
| Tag | Applies to | Description | |-----|------------|-------------| | required:"true" | any | Field must be present and non-zero | | default:"v" | any | Assigned when the field is missing/empty | | min:"5" | number | ≥ 5 | | max:"100" | number | ≤ 100 | | exclusiveMin:"0" | number | > 0 | | exclusiveMax:"100" | number | Slices: enum, const, format, pattern, contains, and notContains apply to each element of a []string. Failures are reported per index — element [2]: .... > > Empty values: those same tags skip empty strings. Combine with required:"true" to also enforce presence.
Conditional Required Tags
Requiredness that depends on sibling fields in the same struct. Referenced fields use their Go field name, not the JSON name.
| Tag | Description | |-----|-------------| | requiredIf:"Type card" | Required when sibling Type equals card | | requiredWith:"Pass" | Required when any listed sibling (comma-separated) is non-empty | | requiredWithout:"Email" | Required when any listed sibling (comma-separated) is empty |
type Payment struct {
Type string `json:"type"`
Card string `json:"card" requiredIf:"Type card"`
Pass string `json:"pass"`
Confirm string `json:"confirm" requiredWith:"Pass"`
Email string `json:"email"`
Phone string `json:"phone" requiredWithout:"Email"`
}
Documentation-Only Tags
These shape the OpenAPI schema; they are not enforced at runtime.
| Tag | Description | |-----|-------------| | description:"..." | Property description | | doc:"..." | Alternative description tag | | example:"v" | Example value | | deprecated:"true" | Mark the property deprecated | | hidden:"true" | Omit the field from the generated schema | | readOnly:"true" | Returned by the server, not sent by clients | | writeOnly:"true" | Sent by clients, not returned | | nullable:"true" | Nullable property (pointer fields are nullable automatically) |
Formats
All formats apply to string fields and to each element of a []string.
Date & time
| Format | Description | |--------|-------------| | date | YYYY-MM-DD | | date-time | RFC3339 timestamp | | time | RFC3339 full-time (15:04:05Z) | | duration | Go duration (1h30m, 300ms) |
Network, web & identifiers
| Format | Description | |--------|-------------| | email | Valid email address | | hostname | Valid hostname | | ipv4 / ipv6 | IP addresses | | mac | MAC address | | cidr | CIDR notation (192.168.1.0/24) | | uri / uri-reference | Any URI / relative reference allowed | | url | Absolute http/https URL | | uuid / ulid | UUID / ULID | | e164 / phone | E.164 phone number (+14155552671) | | credit-card | Luhn-valid card number | | semver | Semantic version (1.2.3-alpha.1) | | json-pointer | RFC 6901 JSON Pointer | | byte / base64 | Base64-encoded value | | base64url | URL-safe Base64 (padded or unpadded) | | jwt | JSON Web Token (three base64url segments) | | port | TCP/UDP port (1–65535) |
String content
| Format | Description | |--------|-------------| | alpha | Letters only | | alphanumeric | Letters and digits | | numeric | Numeric string (123, -12.5) | | ascii | ASCII only | | lowercase / uppercase | Case constraint | | slug | URL slug (my-post-123) | | hexcolor | #RGB or #RRGGBB | | json | Syntactically valid JSON string |
Geo & time zones
| Format | Description | |--------|-------------| | latitude | Decimal latitude, -90…90 | | longitude | Decimal longitude, -180…180 | | timezone | IANA time zone (America/New_York) |
Custom pattern
Phone string `json:"phone" format:"regex" pattern:"^\\+?[1-9]\\d{1,14}$"`
Example
type CreateUserRequest struct {
Email string `json:"email" required:"true" format:"email" example:"user@example.com"`
Password string `json:"password" minLength:"8" writeOnly:"true" description:"User password"`
Age int `json:"age" exclusiveMin:"0" max:"120" default:"18"`
Website string `json:"website" format:"url"`
Kind string `json:"kind" const:"user"`
Roles []string `json:"roles" minItems:"1" uniqueItems:"true" enum:"admin,editor,viewer"`
Metadata map[string]string `json:"metadata" minProperties:"1" maxProperties:"10"`
ID string `json:"id" format:"uuid" readOnly:"true"`
Internal string `json:"-" hidden:"true"`
}
Five Ways to Validate
1. c.Bind() — bind and validate inside the handler
o.Post("/users", func(c *okapi.Context) error {
var req CreateUserRequest
if err := c.Bind(&req); err != nil {
return c.ErrorBadRequest(err)
}
return c.JSON(http.StatusOK, req)
})
2. okapi.Handle() — typed input, manual response
o.Post("/books", okapi.Handle(func(c *okapi.Context, book *Book) error {
book.ID = nextID()
return c.Created(book)
}),
okapi.DocRequestBody(&Book{}),
okapi.DocResponse(&Book{}),
)
3. okapi.H() — shorthand for Handle
o.Get("/books/{id:int}", okapi.H(func(c *okapi.Context, in *BookDetailInput) error {
b := find(in.ID)
if b == nil {
return c.AbortNotFound("Book not found")
}
return c.OK(b)
})).WithInput(&BookDetailInput{})
4. okapi.HandleIO() — typed input and typed output
type BookEditInput struct {
ID int `json:"id" path:"id" required:"true"`
Body Book `json:"body"`
}
type BookOutput struct {
Status int
Body Book
}
o.Put("/books/{id:int}", okapi.HandleIO(func(c *okapi.Context, in *BookEditInput) (*BookOutput, error) {
b := update(in.ID, in.Body)
if b == nil {
return nil, c.AbortNotFound("Book not found")
}
return &BookOutput{Body: *b}, nil
})).WithIO(&BookEditInput{}, &BookOutput{})
5. okapi.HandleO() — typed output only
type BooksResponse struct {
Body []Book `json:"books"`
}
o.Get("/books", okapi.HandleO(func(c *okapi.Context) (*BooksResponse, error) {
return &BooksResponse{Body: books}, nil
})).WithOutput(&BooksResponse{})
The output struct follows the body-style convention (Status, Body, header/cookie-tagged fields). The response format comes from the client's Accept header, defaulting to JSON.
Schema Helpers
| Helper | Purpose | |--------|---------| | okapi.DocRequestBody(&T{}) | Document the request body schema | | okapi.DocResponse(&T{}) / okapi.DocResponse(201, &T{}) | Document a response schema | | route.WithInput(&T{}) | Input schema (pairs with okapi.H / Handle) | | route.WithOutput(&T{}) | Output schema (pairs with HandleO) | | route.WithIO(&In{}, &Out{}) | Both (pairs with HandleIO) |
Returning Validation Errors
return c.AbortValidationErrors([]okapi.ValidationError{
{Field: "email", Message: "must be a valid email", Value: req.Email},
{Field: "age", Message: "must be >= 18"},
}, "validation failed")
AbortValidationErrors always uses the ValidationErrorResponse shape (an ErrorResponse plus an errors array), regardless of the configured error handler. Use AbortValidationErrorsWithProblemDetail for the RFC 7807 form.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: jkaninda
- Source: jkaninda/okapi-skills
- 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.