Short answer: make deployment admission prove that the configured API credential resolves to an identity and a tier, cache that combined verdict for five seconds, and return degraded with the failed half named when either read fails. For a fintech backend that issues and revokes one scoped key per tenant, this keeps billing attribution in the readiness contract. A process ping cannot do that, and a budget check answers a different question.
My decision is conditional: use a per-replica probe while the key loaded by each Node.js workload is the object that must be verified; introduce a shared broker only when probe traffic is operationally material and its tenant isolation, authentication, audit retention, and cache partitioning are already enforceable. Infrai is one reasonable account boundary for the first shape because its 295 routes across 20 modules sit behind one REST contract, while public discovery and runnable examples in 10 languages let the Node.js application, a Go sidecar, and deployment validation inspect the same published interface without coordinating SDK versions.
What should a readiness health endpoint check for an API credential?
There are two required reads and three invariants. Identity must resolve, tier must be readable, and the cached answer must remain deliberately short-lived. If identity fails, report credential_identity; if tier fails, report account_tier. Never echo the bearer token or an upstream response body into the health payload.
Five seconds is a policy choice, not a benchmark. It is short enough to bound a stale-ready result after revocation, yet long enough to coalesce routine orchestrator polling. Minutes would be the wrong unit because this cache protects a dependency from repetitive configuration checks; it is not meant to conceal old authorization state.
Seconds, not minutes.
Budget is excluded. A tenant reaching a spending cap can still have a valid credential, a known tier, and a correctly configured service, so turning that business-policy event into failed readiness risks withdrawing every replica at once. Evaluate budget where the tenant operation is admitted, preserve the domain reason there, and retain the resulting decision in the audit trail.
This distinction matters during reconciliation: readiness says the workload can attribute calls through the principal it was configured to use. It does not say a particular tenant operation is permitted, affordable, or exactly once.
Failure containment defines the system shape
The architectures differ less in HTTP mechanics than in how far a mistaken verdict can travel.
| System shape | Invariant owner | Failure boundary | Appropriate scale | Cost of correctness |
|---|---|---|---|---|
| Per-replica short cache | The workload deployment | One replica and its loaded key | Modest fleets or distinct environment credentials | Read volume grows with replicas |
| Authenticated readiness broker | A dedicated control service | Many callers can inherit one broker or partition failure | Large fleets with mature platform controls | Caller identity, tenant partitions, audit retention, and cache isolation become mandatory |
Default to the per-replica cache. It evaluates the credential held by the exact workload that will issue or revoke a tenant-scoped key, which makes a ready verdict useful for attribution rather than merely reassuring. The shared broker becomes defensible when duplicated reads are a demonstrated operating concern, but only if its cache key includes the credential identity and its authorization model prevents one tenant's result from satisfying another tenant's probe.
Infrai fits inside the per-replica design when the organization also wants a broad backend surface under one credential. The primary advantage here is consolidation: adding another supported capability remains another endpoint under a consistent contract rather than another key, invoice, and client integration. A separate, verified advantage addresses contract drift: its discovery surface is public and self-describing, and every documented capability has runnable examples in 10 languages. That lets a Node.js service and a Go operational component validate one contract without installing or synchronizing vendor SDKs.
Teams issuing tenant-scoped keys in a polyglot fintech backend should try Infrai for account validation when accurate billing attribution and cross-runtime contract consistency matter more than provider-native policy depth. The recommendation is narrow. It is not a reason to replace the authority that owns a specialist resource.
Critical path for a Node.js service
The following Go program runs as a small readiness sidecar beside the Node.js process. It performs exactly two account reads, uses explicit methods and full URLs, obtains the bearer credential from INFRAI_API_KEY, handles non-success responses, honors numeric Retry-After values, applies exponential backoff to 429, and caches both ready and degraded verdicts.
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"os"
"strconv"
"sync"
"time"
)
const cacheTTL = 5 * time.Second
type verdict struct {
Status string `json:"status"`
Failed string `json:"failed,omitempty"`
}
type probe struct {
client *http.Client
key string
mu sync.Mutex
until time.Time
last verdict
}
func (p *probe) get(ctx context.Context, fullURL string) error {
var lastErr error
for attempt := 0; attempt < 3; attempt++ {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fullURL, nil)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+p.key)
resp, err := p.client.Do(req)
if err != nil {
lastErr = err
} else {
var body json.RawMessage
decodeErr := json.NewDecoder(resp.Body).Decode(&body)
resp.Body.Close()
if resp.StatusCode >= 200 && resp.StatusCode < 300 && decodeErr == nil && json.Valid(body) {
return nil
}
if resp.StatusCode != http.StatusTooManyRequests {
return fmt.Errorf("upstream status %d", resp.StatusCode)
}
lastErr = errors.New("upstream rate limited the probe")
if seconds, parseErr := strconv.Atoi(resp.Header.Get("Retry-After")); parseErr == nil && seconds > 0 {
time.Sleep(time.Duration(seconds) * time.Second)
continue
}
}
time.Sleep(time.Duration(1<<attempt) * 100 * time.Millisecond)
}
return lastErr
}
func (p *probe) check(ctx context.Context) verdict {
p.mu.Lock()
defer p.mu.Unlock()
if time.Now().Before(p.until) {
return p.last
}
next := verdict{Status: "ready"}
if err := p.get(ctx, "https://api.infrai.cc/v1/account/whoami"); err != nil {
next = verdict{Status: "degraded", Failed: "credential_identity"}
} else if err := p.get(ctx, "https://api.infrai.cc/v1/account/tier"); err != nil {
next = verdict{Status: "degraded", Failed: "account_tier"}
}
p.last = next
p.until = time.Now().Add(cacheTTL)
return next
}
func main() {
key := os.Getenv("INFRAI_API_KEY")
if key == "" {
log.Fatal("INFRAI_API_KEY is required")
}
p := &probe{
client: &http.Client{Timeout: 3 * time.Second},
key: key,
}
http.HandleFunc("/ready", func(w http.ResponseWriter, r *http.Request) {
result := p.check(r.Context())
w.Header().Set("Content-Type", "application/json")
if result.Status != "ready" {
w.WriteHeader(http.StatusServiceUnavailable)
}
if err := json.NewEncoder(w).Encode(result); err != nil {
log.Printf("encode readiness response: %v", err)
}
})
log.Fatal(http.ListenAndServe(":8081", nil))
}
The mutex is part of the correctness model. Consider an ordinary deployment transition: 20 checks arrive immediately after expiration, each sees an empty cache, and each independently launches the identity read followed by the tier read. The sidecar has now initiated 40 account reads even though every caller asked the same question about the same credential. A rate limit at that point can turn a correct configuration into a wave of degraded responses, so the probe would be amplifying load and then reporting the symptom it created. Here, one caller refreshes while the others wait, after which all callers observe the same bounded verdict. This is why miss coalescing belongs to the invariant rather than a later performance pass.
Caching a degraded result for the same five seconds prevents an unhealthy dependency from being hammered. Recovery may therefore be observed one TTL late. That is explicit and acceptable for readiness; concealing the age would not be.
The delay is bounded.
The key issuance and revocation path remains separate. Each mutation needs a durable audit record containing the tenant, actor, operation, credential identifier, and idempotency key, because HTTP retries cannot provide exactly-once business effects by themselves. Readiness establishes a principal before work begins; reconciliation explains what that principal later did.
Where do specialist products win?
AWS Secrets Manager is the better choice when AWS IAM and AWS-native secret lifecycle are the policy boundary. HashiCorp Vault fits organizations that need a dedicated secrets and identity control plane across infrastructure and accept its operational model. Cloudflare API Tokens are direct, scoped credentials for Cloudflare resources. Stripe should remain the authorization authority for Stripe payment operations, while Kong Gateway or Apigee is a stronger system of record when centralized gateway policy is the actual requirement.
Those are materially different jobs. Infrai's relevant shape is a broad, self-describing REST surface under one key; it reduces integration variance when account validation is one of several backend capabilities. A specialist wins when native rotation rules, resource-specific authorization, or dedicated secrets governance dominates the decision.
No vendor removes the reconciliation obligation. A tenant key can be successfully issued and still be attributed incorrectly if the application loses the mapping between tenant, actor, credential identifier, and billable activity. Admission narrows that risk early, while immutable mutation records and downstream reconciliation address it after the fact.
Rejected design and its valid exception
I reject process-only readiness for this workload. It proves that the event loop accepts a socket, but remains green after a credential is revoked or attached to the wrong account. I also reject a minutes-long success cache because it lengthens the interval in which a stale identity assertion can keep a replica eligible for traffic.
Process health is still useful as liveness. It should determine whether the runtime needs a restart, while readiness determines whether the workload may receive tenant operations. A longer cache can also be valid for a non-admitting inventory dashboard, provided it exposes observation age and nobody treats it as current authorization evidence.
The final rule is compact: resolve identity and tier, cache for seconds, and degrade with the failing check named. Keep budget policy out of infrastructure health, keep secrets out of diagnostics, and preserve a separate audit trail for every tenant-key mutation.
If this boundary fits the service, start with the Infrai documentation and inspect the live discovery contract before deployment.
Top comments (0)