Personal Go utility packages.
Packages
- fsx - filesystem wrappers and utilities
- pass - multi-scheme password hashing (argon2id default, bcrypt migration, Django import)
- httpx - HTTP middleware, routing, JSON helpers, flash messages, client IP resolution
- migrate - minimal SQL migration runner with multi-layer fs.FS support
- slogx - structured logging helpers wrapping
log/slog
- versionx - VCS build info version extraction
pass
godoc
Multi-scheme password hashing with transparent migration. Argon2id is the
default; bcrypt is accepted for migrating existing stores. Django's
argon2$argon2id$... hash format is also supported.
// Hash a new password (argon2id).
hash, err := pass.Hash(password)
// Verify on login, rehash transparently if needed.
if err := pass.Verify(stored, input); err != nil {
return err // pass.ErrMismatch or internal error
}
if pass.NeedsUpdate(stored) {
if newHash, err := pass.Hash(input); err == nil {
_ = db.UpdatePasswordHash(ctx, userID, newHash)
}
}
// Custom context: argon2id with tuned params, bcrypt for migration.
ctx := pass.NewContext(
pass.Argon2id{Memory: 128 * 1024, Iterations: 2},
pass.Bcrypt{},
)
fsx
godoc
Filesystem wrappers and utilities.
NoListFS wraps any http.FileSystem and disables directory listings: any
directory without an index.html returns a 404 instead of a file listing.
//go:embed static
var staticFiles embed.FS
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(
fsx.NoListFS{FS: http.FS(staticFiles)},
)))
httpx
godoc
HTTP server utilities built on top of net/http: a router with middleware
support and route grouping, JSON request/response helpers, flash messages,
client IP resolution behind trusted proxies, and graceful shutdown.
logger := slogx.New(slogx.FormatPretty, slogx.LevelInfo)
r := httpx.NewRouter(logger)
r.Use(httpx.WithRequestID, httpx.Logging(logger), httpx.Recovery(logger))
r.Handle("GET /api/hello", func(w http.ResponseWriter, r *http.Request) {
httpx.RespondJSON(w, http.StatusOK, map[string]string{"hello": "world"})
})
r.Group(func(r *httpx.Router) {
r.Use(requireAuth)
r.Handle("POST /api/items", createItem)
})
ctx, cancel := httpx.WithSignalShutdown(context.Background(), logger)
defer cancel()
r.ListenAndServe(ctx, ":8080")
migrate
godoc
Minimal SQL migration runner. Migrations are .sql files read from one or
more named Layer instances (each wrapping an fs.FS) and applied in
lexicographic order across all layers. Applied versions are tracked in a
schema_migrations table. SQLite and PostgreSQL dialects are included.
//go:embed migrations
var migrations embed.FS
db, _ := sql.Open("sqlite3", "app.db")
m := migrate.New(
migrate.NewDriver(db, migrate.SQLiteDialect{}),
migrate.WithLayers(migrate.Layer{Name: "app", FS: migrations}),
)
result, err := m.Migrate(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("applied %d migrations\n", len(result.Applied))
slogx
godoc
Wraps log/slog with a few conveniences: a New function that creates and
sets the default logger in one call, context-based logger passing, and a
Level and Format type that both implement flag.Value for easy CLI
wiring.
var (
logLevel slogx.Level = slogx.LevelInfo
logFormat slogx.Format = slogx.FormatPretty
)
flag.Var(&logLevel, "log-level", "debug, info, warn, error")
flag.Var(&logFormat, "log-format", "pretty, json, text")
flag.Parse()
logger := slogx.New(logFormat, logLevel)
logger.Info("started", slogx.String("version", version))
// Attach to context and retrieve downstream.
ctx = slogx.WithLogger(ctx, logger)
slogx.FromContext(ctx).Info("handling request", slogx.Err(err))
versionx
godoc
Extracts the application version from Go build info. Returns the VCS
revision (plus -dirty if the tree was modified), falling back to "dev"
or "unknown". Accepts an optional override for ldflags stamping.
// main.go
var version string // set via -ldflags "-X main.version=v1.2.3"
func main() {
fmt.Println(versionx.Get(version)) // ldflags value if set, else VCS revision
fmt.Println(versionx.GoVersion()) // e.g. "go1.22.0"
}
License
MIT