A little care for every API call

API calls fail.
Your response
can adapt.

Budgeted retries, dependency breakers, and adaptive timeouts for Go's outbound HTTP calls. One transport. No extra service to run.

Go 1.23+Zero runtime dependenciesApache-2.0

Resilience, in motion.

Enforce mode

A replay-safe read is sent.

An eligible read gets at most one extra attempt, only when the diagnosis and retry budgets allow it.

What does
self-healing mean?

Responding to failure automatically, within safe limits. Curo adapts how your service calls an API, not the API itself. It can't fix an upstream outage. It can help your service handle one.

01 / The approach

More than a
circuit breaker.

Not just when to stop. When to try again, how long to wait, and when to let traffic return.

01

A second chance.
Not ten more.

One retry for an eligible, replay-safe read. Diagnosis and budgets decide whether it can help.

Budgeted retries
02

A sensible wait.
Not a static guess.

Reads get a response-header timeout learned from recent latency, within bounds you set. Earlier caller deadlines always win.

Adaptive timeouts
03

Room to recover.
Not more pressure.

Once a dependency is diagnosed as down, calls fail fast. After each cooldown, a probe tests recovery, and traffic returns when one succeeds.

Dependency breakers

Deterministic rules, not AI. Decisions come with evidence and reasons.

02 / Start small

Observe first.
Act when you're ready.

Wrap your existing transport. Curo starts in Observe, showing its decisions without changing request behavior.

  1. 1 Wrap
  2. 2 Observe
  3. 3 Enforce
go get github.com/raj1kshtz/curo@latest
client.go Observe by default
transport, err := curo.New(
    http.DefaultTransport,
)
if err != nil {
    return err
}

client := &http.Client{
    Transport: transport,
    Timeout:   time.Minute,
}

// Review Stats and Report, then:
// transport.SetMode(curo.Enforce)

// Close transport on shutdown.
v0.1.0 Go 1.23+ Standard library only

03 / A little clarity

Good questions.
Clear answers.

The short version, before
you dive into the docs.

Who is Curo for?

Go developers whose services call HTTP APIs. Curo wraps outbound net/http traffic, not incoming handlers. It is not a gRPC interceptor, a proxy, or a separate service.

When does it change request behavior?

Only in Enforce mode, which you choose with WithMode at startup or SetMode at runtime. Each control still needs its own evidence and safety checks. Observe is the default, and switching back to it stops every action. Off passes requests straight through.

What stays under my control?

Your HTTP client, the operating mode, and the timeout bounds, which default to 2 and 30 seconds. Keep your client's timeout above that ceiling. Every other threshold is fixed. Curo never extends a caller's deadline or retries a write.

How do I see what it decides?

Stats counts requests, retries, breaker actions, timeouts, and Curo's own failures. Report shows each dependency's readiness, diagnosis, reasons, evidence, and timeout. Both encode to JSON, so a private debug endpoint can serve them.

Which errors should I handle?

In Enforce mode, ErrBreakerOpen means Curo did not send the request, and ErrTimeout means the dependency may have received it. Match both with errors.Is, checking ErrTimeout before context.DeadlineExceeded, which it also matches.

What if Curo itself fails?

A failure inside Curo never fails a request. After three internal failures within a minute, the transport passes every request straight through for the rest of its life. Stats reports this, and WithLogger logs the failures through log/slog.

What are its limits?

State belongs to one transport in one process, so replicas learn separately and restarts start cold. A transport tracks up to 128 targets, each a scheme, host, port, and method class, and requests beyond them get no actions. Retries and breakers wait for about 30 seconds of steady traffic.

How mature is the library?

v0.1.0 is the first release. CI tests every change on Linux, macOS, and Windows with Go 1.23 and the two latest Go releases, under the race detector, and requires every statement to run. Until v1, a minor release may still change the public API, so pin the version you adopt.

The behavior, boundaries, and fine print (opens in a new tab)

Small integration. Thoughtful safeguards.

A little more resilience.
A little less guesswork.

Meet Curo on GitHub (opens in a new tab)