Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 21 additions & 12 deletions e2e/loadtest_harness_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -581,16 +581,24 @@ func extractJWTLoose(note string) string {
// teardown mechanism and the ultimate backstop.
//
// To NOT rely solely on TTL, the harness actively tears Lane-B resources
// down: every anonymous provision response carries a fingerprint-scoped
// onboarding JWT in `note`. POST /claim with that JWT moves EVERY active
// resource for that fingerprint into a fresh throwaway team in one call;
// each is then deletable via the authenticated DELETE. Because a Lane-B
// burst uses a single fingerprint, one claim + a delete-sweep reclaims the
// whole burst. If E2E_JWT_SECRET is unset (cannot mint the session JWT to
// authorize the DELETEs) the harness falls back to the 24h TTL and says so.
// down: every anonymous provision response carries an onboarding JWT in
// `note`. POST /claim with that JWT moves the resources THAT JWT NAMES into a
// fresh throwaway team; each is then deletable via the authenticated DELETE.
// If E2E_JWT_SECRET is unset (cannot mint the session JWT to authorize the
// DELETEs) the harness falls back to the 24h TTL and says so.
//
// teardownAnonymousFingerprint claims every resource on `fpJWT`'s fingerprint
// and deletes them. Returns (claimed, deleted, ok).
// PARTIAL SINCE 2026-08-13: this used to reclaim the WHOLE burst from one JWT,
// because /claim swept every resource sharing the caller's fingerprint. That
// sweep was a cross-tenant ownership hole (SHA256(/24 + ASN) buckets strangers
// behind one NAT together) and was removed — see
// api/internal/handlers/onboarding.go. A single captured JWT now reclaims only
// its own token (plus anything chained onto it via X-Instant-Upgrade-Token,
// which this concurrent burst does not thread). The remainder of the burst
// falls back to the 24h TTL, which the caller already logs. Reclaiming the
// full burst again would mean chaining the header through the goroutines.
//
// teardownAnonymousFingerprint claims the resources named by `fpJWT` and
// deletes them. Returns (claimed, deleted, ok).
func teardownAnonymousFingerprint(t *testing.T, fpJWT string) (claimed, deleted int, ok bool) {
t.Helper()
if fpJWT == "" {
Expand Down Expand Up @@ -797,12 +805,13 @@ func TestLoad_FingerprintDedup_UnderBurst(t *testing.T) {

stats := newLoadStats()
// fpJWT captures one onboarding JWT from a 201 response so the test can
// claim+delete every anonymous resource it created on this fingerprint
// (rather than relying purely on the 24h TTL).
// claim+delete the resource that JWT names (rather than relying purely on
// the 24h TTL). See teardownAnonymousFingerprint for why this is partial
// coverage of the burst since the claim-by-fingerprint fix.
var fpJWT string
var fpJWTMu sync.Mutex

// Cleanup: claim this fingerprint's burst into a throwaway team & delete.
// Cleanup: claim what the captured JWT names into a throwaway team & delete.
t.Cleanup(func() {
fpJWTMu.Lock()
jwtTok := fpJWT
Expand Down
4 changes: 2 additions & 2 deletions internal/handlers/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func (h *CacheHandler) NewCache(c *fiber.Ctx) error {
return h.denyProvisionOverCap(c, fp, "redis")
}
if err == nil {
jwtToken, jti, jwtErr := h.issueOnboardingJWT(ctx, fp, country, vendor, "redis", []string{existing.Token.String()})
jwtToken, jti, jwtErr := h.issueOnboardingJWT(c, fp, country, vendor, "redis", []string{existing.Token.String()})
if jwtErr == nil && jti != "" {
if evErr := h.createOnboardingEvent(ctx, fp, jti, existing.Token); evErr != nil {
slog.Error("cache.new.onboarding_event_failed_limit_path", "error", evErr, "request_id", requestID)
Expand Down Expand Up @@ -239,7 +239,7 @@ func (h *CacheHandler) NewCache(c *fiber.Ctx) error {
return respondProvisionFailed(c, finErr, "Failed to persist Redis resource")
}

jwtToken, jti, jwtErr := h.issueOnboardingJWT(ctx, fp, country, vendor, "redis", []string{tokenStr})
jwtToken, jti, jwtErr := h.issueOnboardingJWT(c, fp, country, vendor, "redis", []string{tokenStr})
if jwtErr != nil {
slog.Error("cache.new.jwt_issue_failed", "error", jwtErr, "request_id", requestID)
}
Expand Down
Loading
Loading