Files
qtalker---/backend/app/ws.py
Indiana 6f2905c2f0 fix: the ritual and judgment were faucets too
The Passage faucet was not unique. The same shape — a client-sendable
message that rewinds the guarding state — sat in both siblings, and for the
same reason: nothing drove these handlers over a real connection.

`ritual_start` resets `ritual_completed`, and the UI offers that button
(honest play needs it after a failed roll). Every re-walk re-credited
RITUAL_SUCCESS_ESSENCE and re-rolled an item drop. `ritual_step` has no
limiter at all.

Judgment was worse. Only `cross_over` was guarded; replaying any other
verdict paid CORRECT_JUDGMENT_ESSENCE plus favor plus an item roll on every
frame, unbounded. Favor is the damaging half — it pins at the +1.0 ceiling
and then biases every future mint through apply_favor_bias, so the exploit
permanently changed which spirits the seeker can meet. The frame also echoed
the nominal deltas, which seance.tsx sums into displayed totals, so the
screen and the ledger diverged.

Guards mirror `passage_paid`: cleared only by a genuine summon, never by the
rewinding message. `judged_verdicts` is keyed per verdict rather than a
blanket latch, so correcting a wrong call still resolves; `test` is exempt
since it never touches the ledger. Both frames now report what was applied.

tests/test_ws_reward_replay.py drives each exploit and reads the ledger from
the database. Proven both directions: reverting the guard fails with
"minted 120 extra essence"; over-broadening it fails with "a fresh presence
did not re-open the purse".

Audited and found safe, with reasons in the report: summon trickle, device
telemetry ingestion, at_peace writes, purchase_unlock, sigils, waitlist,
scry/question/anomaly/manifest/fragment. Every essence write is
with_for_update-locked.

Known residual: guards live on SeanceState, so a reconnect resets them —
but each payout still needs a summon, and summon_limiter is per user across
connections, so income stays rate-bounded. Not closed, deliberately.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 02:46:56 +00:00

60 KiB