feat(reveal-sessions-expired): worker sweeper closes reveal-session windows (Slice M3)#9
Merged
Conversation
…indows (Slice M3)
Periodic sweeper that turns reveal_sessions.expires_at into a hard
boundary on wrap retrievability. Stamps the session row as expired
(reason='ttl') via the M1 storage layer's atomic UPDATE … RETURNING,
then advances every wrap_id from the swept sessions to expires_at=now
so any post-expire retrieve returns ErrExpired — even if the SPA still
holds the wrap_id across a tab refresh.
Service surface:
RevealSessionsExpired{Sessions, Wraps, Notifier, Now}.Run(ctx)
Wired into cmd/worker/main.go alongside the existing five sweepers
(wraps-expired, secrets-stale, agents-stale, jobs-recovery,
discover-scheduler). Defaults:
SB_WORKER_REVEAL_SESSIONS_EXPIRED_INTERVAL=5s
5s is faster than the default 60s used by other sweepers because
reveal sessions are user-facing — a session ending late means a wrap
could still be retrievable after the SPA already nuked its plaintext
display. The query is a single indexed UPDATE … RETURNING so the
cadence cost is negligible.
Failure semantics:
- Sweep failure → error returned, error-severity notification, no
wrap mutation attempted.
- Per-wrap SetExpiry failures → joined via errors.Join, warn-severity
notification with sessions_swept + wraps_expired + wrap_errors
counts; a single broken wrap MUST NOT drop the rest of the sweep.
- Zero rows → no notification (matches WrapsExpired's posture).
Tests (5 unit tests under -race):
- happy path: 2 sessions / 3 wraps → SweepExpired called once,
SetExpiry called 3× with now timestamp, info notification fires
- idempotent zero-rows: empty sweep result → no SetExpiry calls,
no notifications
- partial wrap-expiry failure: 1 of 3 wraps fails SetExpiry → error
returned, all 3 attempted (no early exit), warn notification with
'wrap-expiry errors'
- sweep failure: storage error → error returned, no wrap mutation,
failure notification
- Name() returns 'reveal-sessions-expired'
Closes #8. Part of secrets-bridge/api#71 (EPIC: Slice M).
Depends on: M1 (secrets-bridge/api#82 — schema + RevealSessionRepository
+ SweepExpired), M2 (secrets-bridge/api#83 — service contract that
opens the sessions this sweeper closes).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #8. Part of secrets-bridge/api#71 (EPIC Slice M).
Summary
Periodic sweeper that turns
reveal_sessions.expires_atinto a hard boundary on wrap retrievability. Stamps the session row as expired (reason='ttl') via the M1 storage layer's atomicUPDATE … RETURNING, then advances everywrap_idfrom the swept sessions toexpires_at=nowso any post-expire retrieve returnsErrExpired— even if the SPA still holds the wrap_id across a tab refresh.Sweeper surface
Wired into
cmd/worker/main.goalongside the existing five sweepers (wraps-expired,secrets-stale,agents-stale,jobs-recovery,discover-scheduler).Config knob
5s is faster than the default 60s used by other sweepers because reveal sessions are user-facing — a session ending late means a wrap could still be retrievable after the SPA already nuked its plaintext display. The underlying query is a single indexed
UPDATE … RETURNINGso the cadence cost is negligible.Failure semantics
SetExpiryfailureserrors.Join; warn-severity notification withsessions_swept+wraps_expired+wrap_errorscounts; a single broken wrap MUST NOT drop the rest of the sweep.WrapsExpired's posture).Closes the session-level invariant
Tests (5 unit tests, all passing under
-race)SweepExpiredcalled once,SetExpirycalled 3× withnowtimestamp, info notification fires.SetExpirycalls, no notifications.SetExpiry→ error returned, all 3 attempted (no early exit), warn notification with'wrap-expiry errors'.Name()returns'reveal-sessions-expired'.Test plan
go build ./...cleango vet ./...cleango test -race -count=1 ./...all packages greengo mod tidy(no drift)expires_atcountdownDependencies
RevealSessionRepository+SweepExpired