{"id":50687739,"url":"https://github.com/klarlabs-studio/fortify","last_synced_at":"2026-06-09T00:04:19.915Z","repository":{"id":317266903,"uuid":"1066595554","full_name":"klarlabs-studio/fortify","owner":"klarlabs-studio","description":"Production-grade resilience for Go services calling LLMs and tools. Composable patterns — circuit breaker, retry, rate limit, timeout, bulkhead, fallback, hedge, adaptive concurrency, cost budget, stream timeout — with first-class OpenTelemetry, Prometheus, and slog observability. Zero core deps.","archived":false,"fork":false,"pushed_at":"2026-06-06T19:28:27.000Z","size":24692,"stargazers_count":0,"open_issues_count":2,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-06T21:14:56.609Z","etag":null,"topics":["ai-agents","bulkhead","circuit-breaker","fault-tolerance","go","golang","hedging","llm","microservices","observability","opentelemetry","prometheus","rate-limiting","resilience","retry-strategies","timeout"],"latest_commit_sha":null,"homepage":"https://klarlabs-studio.github.io/fortify/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/klarlabs-studio.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":"GOVERNANCE.md","roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-29T17:45:16.000Z","updated_at":"2026-06-06T19:28:28.000Z","dependencies_parsed_at":"2025-10-19T11:25:06.337Z","dependency_job_id":"5b43f452-6dc4-450d-ab4d-b762ba364125","html_url":"https://github.com/klarlabs-studio/fortify","commit_stats":null,"previous_names":["felixgeelhaar/fortify","klarlabs-studio/fortify"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/klarlabs-studio/fortify","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klarlabs-studio%2Ffortify","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klarlabs-studio%2Ffortify/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klarlabs-studio%2Ffortify/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klarlabs-studio%2Ffortify/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/klarlabs-studio","download_url":"https://codeload.github.com/klarlabs-studio/fortify/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klarlabs-studio%2Ffortify/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34085335,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-08T02:00:07.615Z","response_time":111,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agents","bulkhead","circuit-breaker","fault-tolerance","go","golang","hedging","llm","microservices","observability","opentelemetry","prometheus","rate-limiting","resilience","retry-strategies","timeout"],"created_at":"2026-06-09T00:04:17.523Z","updated_at":"2026-06-09T00:04:19.903Z","avatar_url":"https://github.com/klarlabs-studio.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"assets/fortify.png\" alt=\"Fortify Logo\" width=\"200\"/\u003e\n  \u003ch1\u003eFortify\u003c/h1\u003e\n\u003c/div\u003e\n\n[![Go Reference](https://pkg.go.dev/badge/go.klarlabs.de/fortify.svg)](https://pkg.go.dev/go.klarlabs.de/fortify)\n[![Go Report Card](https://goreportcard.com/badge/go.klarlabs.de/fortify)](https://goreportcard.com/report/go.klarlabs.de/fortify)\n[![CI Status](https://github.com/klarlabs-studio/fortify/workflows/CI/badge.svg)](https://github.com/klarlabs-studio/fortify/actions/workflows/ci.yml)\n[![Coverage](./assets/coverage-badge.svg)](./assets/coverage-badge.svg)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Go Version](https://img.shields.io/github/go-mod/go-version/klarlabs-studio/fortify)](https://github.com/klarlabs-studio/fortify)\n[![Release](https://img.shields.io/github/v/release/klarlabs-studio/fortify)](https://github.com/klarlabs-studio/fortify/releases)\n\nComposable resilience patterns for Go: circuit breaker, retry, rate limit, timeout, bulkhead, fallback, hedge, adaptive concurrency. First-class observability via OpenTelemetry, Prometheus, and `slog`. Zero dependencies in the core.\n\n## Install\n\n```bash\ngo get go.klarlabs.de/fortify\n```\n\nMinimum Go version is declared in [`go.mod`](./go.mod). The Go Version badge above always reflects the current value.\n\n## 60-second quick start\n\nWrap an outbound call with circuit breaker + retry + timeout in one line, using a preset.\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"log\"\n    \"time\"\n\n    \"go.klarlabs.de/fortify/middleware\"\n)\n\ntype Response struct {\n    Body string\n}\n\nfunc callDownstream(ctx context.Context) (Response, error) {\n    // your real client call here\n    return Response{Body: \"ok\"}, nil\n}\n\nfunc main() {\n    chain, err := middleware.RPCDownstream[Response](middleware.RPCDownstreamConfig{\n        CallTimeout: time.Second,\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    result, err := chain.Execute(context.Background(), callDownstream)\n    log.Printf(\"result=%+v err=%v\", result, err)\n}\n```\n\nA `Response` struct is used instead of a bare `string` so the example mirrors what real services actually return — your handler will likely look closer to this than to the toy `[string]` form.\n\nFor a hand-rolled chain combining all eight patterns, see [`examples/composition`](./examples/composition/). For deciding which pattern fits which symptom, see the [pattern decision tree](docs/concepts.md#pattern-decision-tree).\n\n## Why Fortify\n\nMost Go resilience libraries cover a single pattern. Stitching together a circuit breaker (`sony/gobreaker`), a retry policy (`hashicorp/go-retryablehttp`), and a rate limiter (`golang.org/x/time/rate`) means three different APIs, three different observability stories, and ad-hoc composition.\n\nFortify is the resilience library for teams that want **all of it under one roof**, with consistent ergonomics and observability built in.\n\nSee [docs/COMPARISON.md](docs/COMPARISON.md) for a detailed comparison against `sony/gobreaker`, `failsafe-go`, `uber-go/ratelimit`, `golang.org/x/time/rate`, and `hashicorp/go-retryablehttp`. See [docs/POSITIONING.md](docs/POSITIONING.md) for the project's wedge and validation gates.\n\n## Patterns at a glance\n\n| Pattern         | Package           | When to use                                                       |\n| --------------- | ----------------- | ----------------------------------------------------------------- |\n| Circuit breaker | `circuitbreaker/` | Stop hammering an unhealthy downstream                            |\n| Retry           | `retry/`          | Recover from transient failures with backoff                      |\n| Rate limit      | `ratelimit/`      | Cap requests per key (token bucket, pluggable storage)            |\n| Timeout         | `timeout/`        | Bound operation latency                                           |\n| Bulkhead        | `bulkhead/`       | Cap concurrency to prevent resource exhaustion                    |\n| Fallback        | `fallback/`       | Graceful degradation when the primary path fails                  |\n| Hedge           | `hedge/`          | Reduce tail latency by firing parallel attempts on slow primary   |\n| Adaptive concurrency | `adaptive/`  | AIMD / Vegas / Gradient2 auto-tuning of concurrency cap            |\n\nFor the semantics behind each pattern see [docs/concepts.md](docs/concepts.md).\n\n## Pre-built bundles\n\nFor common shapes, use a preset instead of hand-rolling a chain:\n\n```go\n// Outbound HTTP client with CB + retry + timeout\nchain, _ := middleware.HTTPClient(middleware.HTTPClientConfig{Timeout: 5 * time.Second})\n\n// As an http.RoundTripper, mountable on http.Client.Transport\nrt, _ := middleware.HTTPRoundTripper(nil, middleware.HTTPClientConfig{Timeout: 5 * time.Second})\n\n// Database query with conservative retry\nchain, _ := middleware.DatabaseQuery(middleware.DatabaseQueryConfig{QueryTimeout: 200 * time.Millisecond})\n\n// Per-downstream RPC chain (one chain per downstream)\nchain, _ := middleware.RPCDownstream[Response](middleware.RPCDownstreamConfig{CallTimeout: 1 * time.Second})\n\n// Server-side handler wrapper (rate limit + CB + timeout)\nh, _ := middleware.HTTPHandler(myHandler, middleware.HTTPHandlerConfig{Timeout: 1 * time.Second})\n```\n\nPresets are starting points. Build your own `middleware.Chain` when the preset doesn't fit.\n\n## Composition\n\nCombine patterns via `middleware.Chain`:\n\n```go\nimport \"go.klarlabs.de/fortify/middleware\"\n\nchain := middleware.New[Response]().\n    WithBulkhead(bh).\n    WithRateLimit(rl, \"user-key\").\n    WithTimeout(tm, 5*time.Second).\n    WithCircuitBreaker(cb).\n    WithRetry(r)\n\nresult, err := chain.Execute(ctx, func(ctx context.Context) (Response, error) {\n    return makeRequest(ctx)\n})\n```\n\nOrder matters. Outer-to-inner: `Bulkhead → RateLimit → Timeout → CircuitBreaker → Retry → operation`. Rationale and pitfalls in [docs/how-to-compose.md](docs/how-to-compose.md).\n\n## Integrations\n\n- HTTP middleware (`fortify/http`): `RateLimit`, `Timeout`, `CircuitBreaker` decorators\n- gRPC interceptors (`fortify/grpc`): unary + streaming\n- OpenTelemetry tracing (`fortify/otel`)\n- Prometheus metrics (`fortify/metrics`)\n- Structured logging (`fortify/slog`)\n- Chaos testing (`fortify/testing`)\n\nSee [docs/integrations.md](docs/integrations.md) for HTTP and gRPC, [docs/how-to-observe.md](docs/how-to-observe.md) for telemetry.\n\n## Performance\n\nFast paths are designed to be sub-microsecond and zero-alloc. Apple M5, Go 1.25:\n\n| Pattern (steady-state) | Overhead | Allocs |\n| ---------------------- | -------- | ------ |\n| Circuit breaker (Closed, lock-free) | ~70ns | 0 |\n| Retry (no retry needed) | ~25ns | 0 |\n| Rate limit `Allow` (in-process Store) | ~200ns | 3 |\n| Timeout | ~50ns | 0 |\n| Bulkhead `Execute` (slot available) | ~39ns | 0 |\n\nThe circuit breaker takes a lock-free fast path in steady-state Closed (atomic mirrors of state, expiry, generation). Concurrent measurements (10 goroutines): ~187ns/op, 0 allocs.\n\n## Documentation\n\n- **Concepts** — [docs/concepts.md](docs/concepts.md) — what each pattern does and when to use it\n- **How-to: compose** — [docs/how-to-compose.md](docs/how-to-compose.md) — chain ordering, pitfalls\n- **How-to: observe** — [docs/how-to-observe.md](docs/how-to-observe.md) — `slog`, OTel, Prometheus\n- **How-to: rate limit** — [docs/how-to-rate-limit.md](docs/how-to-rate-limit.md) — per-key, custom Store, KeyFunc\n- **How-to: test** — [docs/how-to-test.md](docs/how-to-test.md) — chaos utilities, regression testing\n- **Integrations** — [docs/integrations.md](docs/integrations.md) — HTTP and gRPC\n- **Production checklist** — [docs/PRODUCTION.md](docs/PRODUCTION.md)\n- **Error handling** — [docs/ERROR_HANDLING.md](docs/ERROR_HANDLING.md)\n- **Migration notes** — [docs/MIGRATION.md](docs/MIGRATION.md)\n- **API reference** — [pkg.go.dev](https://pkg.go.dev/go.klarlabs.de/fortify)\n\n## Project governance\n\n- [GOVERNANCE.md](GOVERNANCE.md) — maintainership, decision-making, semver policy\n- [ADOPTERS.md](ADOPTERS.md) — production users; PRs welcome\n- [SECURITY.md](SECURITY.md) — vulnerability disclosure\n- [CHANGELOG.md](CHANGELOG.md) — release notes\n\n## Examples\n\n- [Basic patterns](./examples/basic/) — one file per pattern\n- [HTTP server](./examples/http/) — middleware integration\n- [Composition](./examples/composition/) — full chain in production-shape\n- [MCP server](./examples/mcp-server/) — resilience for an MCP tool handler\n- [Eino + LLMCall](./examples/eino/) — wrap an Eino chat model with cost-budgeted resilience\n- [Observability demo](./examples/observability-demo/) — Prometheus + Grafana stack with a pre-built Fortify dashboard (`docker compose up --build`)\n\n## Contributing\n\nPRs welcome. Please:\n\n1. Open an issue for non-trivial changes before writing code\n2. Add tests with `-race` for new functionality\n3. Run `go test -race ./...` and `golangci-lint run` before pushing\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n\n## Acknowledgments\n\nConcepts borrowed from [Hystrix](https://github.com/Netflix/Hystrix) (Java/Netflix), [resilience4j](https://github.com/resilience4j/resilience4j) (Java), and [Polly](https://github.com/App-vNext/Polly) (.NET). Closest Go analogue: [failsafe-go](https://github.com/failsafe-go/failsafe-go); see the [comparison](docs/COMPARISON.md).\n\n## Support\n\n- [Issues](https://github.com/klarlabs-studio/fortify/issues) — bug reports and feature requests\n- [Discussions](https://github.com/klarlabs-studio/fortify/discussions) — questions and design conversations\n- [API reference](https://pkg.go.dev/go.klarlabs.de/fortify) — pkg.go.dev\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fklarlabs-studio%2Ffortify","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fklarlabs-studio%2Ffortify","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fklarlabs-studio%2Ffortify/lists"}