passgo is a zero-dependency Go library for password validation with a small API, predictable performance, estimated entropy, reusable blacklists, and optional user-context checks with prepared reuse support.
- zero external dependencies
- fast path for ASCII passwords
- configurable password policies
- detailed validation output when needed
- concurrency-safe validator instances
The library is intentionally small and split into a few focused pieces:
Policy: declarative password requirementsContext: optional user-specific data such as name, email, or domainPreparedContext: reusable compiled form ofContextfor hot pathsValidator: compiled, immutable validator that can be reused across goroutinesViolation: bitmask for the fast validation pathResult: detailed output with counts, estimated entropy, patterns, and human-readable issuesEntropy: helper for standalone entropy estimationBlacklist: reusable compiled blacklistOption: optional features such as attaching a blacklist
Validation works in one scan of the password. For ASCII-only passwords it stays on a byte-oriented fast path; if non-ASCII bytes are found it falls back to rune-aware scanning using the standard library only.
Check only computes the analyses required by the active policy. Analyze always computes the full detailed result. Validate is kept as a convenience alias for Analyze.
- minimum and maximum length
- minimum lowercase letters
- minimum uppercase letters
- minimum digits
- minimum symbols
- minimum estimated entropy
- maximum repeated-character run
- maximum sequential run for ASCII letters and digits
- maximum keyboard-pattern run for rows such as
qwerty,asdf,zxcvbn, and!@#$ - whitespace policies: deny any, deny leading/trailing, ignore for entropy
- optional case-insensitive exact blacklist
- optional rejection of substrings derived from
Context.UserData - optional ASCII-only enforcement
go get github.com/godeh/passgopackage main
import (
"fmt"
"github.com/godeh/passgo"
)
func main() {
blacklist := passgo.NewBlacklist("password123!", "admin123!")
validator := passgo.Must(passgo.Policy{
MinLength: 12,
MinLower: 2,
MinUpper: 1,
MinDigits: 2,
MinSymbols: 1,
MinEntropyBits: 45,
MaxRepeatedRun: 2,
MaxSequentialRun: 3,
MaxKeyboardPatternRun: 3,
WhitespacePolicy: passgo.WhitespaceDenyLeadingOrTrailing | passgo.WhitespaceIgnoreForEntropy,
RejectUserData: true,
}, passgo.WithCompiledBlacklist(blacklist))
result := validator.AnalyzeWithContext("S7r!xQ2#Lm9$", passgo.Context{
UserData: []string{"raul", "raul@example.com"},
})
fmt.Println(result.Valid)
fmt.Println(result.EstimatedEntropyBits)
fmt.Println(result.Violations)
fmt.Println(result.HasCode(passgo.CodeContainsUserData))
}Use Check when you only need the bitmask:
mask := validator.CheckWithContext("S7r!xQ2#Lm9$", passgo.Context{
UserData: []string{"raul"},
})
if mask.Has(passgo.ViolationMissingSymbols) {
// handle error
}When the same user data is reused many times, prepare it once:
prepared := validator.PrepareContext(passgo.Context{
UserData: []string{"raul", "raul@example.com"},
})
mask := validator.CheckWithPreparedContext("S7r!xQ2#Lm9$", prepared)
result := validator.AnalyzeWithPreparedContext("S7r!xQ2#Lm9$", prepared)
_ = mask
_ = resultUse Analyze when you want the full report explicitly:
result := validator.Analyze("pass 123!")
for _, issue := range result.Issues() {
fmt.Println(issue.Code, issue.Message)
}passgo estimates entropy from the observed character classes and applies lightweight penalties for low uniqueness, repeated runs, sequential runs, and keyboard patterns. The result is intentionally lightweight and deterministic, suitable for policy checks and coarse strength scoring.
It is an estimate, not a promise about real-world crack resistance.
Whitespace can optionally be ignored for entropy calculations via WhitespaceIgnoreForEntropy.
Every human-readable issue also carries a stable machine-readable IssueCode, for example:
too_shortmissing_upperkeyboard_pattern_too_longwhitespace_not_allowedcontains_user_data
This makes it easier to integrate passgo with APIs and frontends without relying on free-form error messages.
See the practical examples in:
examples/basicexamples/entropyexamples/blacklistexamples/contextexamples/httpexamples/issuesexamples/patternsexamples/presetsexamples/whitespace
- localized issue messages
- custom rule hooks outside the hot path