handleCreateSession in internal/handlers/api.go calls validateHashcash first, and Validator.Validate (internal/hashcash/hashcash.go) records the stamp as spent as soon as it is valid. The nick check, the username check and CreateSession's unique-nick check come after. A request answered 400invalid nick format or 409nick already taken has spent its stamp, and the same stamp retried gets 402stamp already used: at the default 20 bits the user pays another half second to two seconds of proof-of-work for a nick collision. On a +H channel, checkChannelHashcash (internal/service/service.go) records the stamp before the membership, ban and +m checks in SendChannelMessage, so a message refused with 404 has spent a stamp on nothing. README.md (Why Hashcash) says a legitimate user "pays once"; the error tables say nothing about a refused request consuming the stamp. Verified by reading the order of calls; cost to the client, not corruption.
Fix: check the proof first and claim the stamp last. For session creation, split Validate into a check and a claim, claim just before CreateSession and release the claim if the insert fails; for channel messages, do the membership, ban and +m checks before the claim. With #150, which makes the claim the replay check, the claim is then the last step before the message is stored.
Definition of done
A POST /api/v1/session refused with 400 or 409 leaves its stamp usable, and the same stamp then creates a session.
A +H PRIVMSG refused for membership, ban or +m leaves its stamp usable.
Tests for both.
One reviewed PR against next.
Model: fable-5-1
`handleCreateSession` in `internal/handlers/api.go` calls `validateHashcash` first, and `Validator.Validate` (`internal/hashcash/hashcash.go`) records the stamp as spent as soon as it is valid. The nick check, the username check and `CreateSession`'s unique-nick check come after. A request answered `400` `invalid nick format` or `409` `nick already taken` has spent its stamp, and the same stamp retried gets `402` `stamp already used`: at the default 20 bits the user pays another half second to two seconds of proof-of-work for a nick collision. On a `+H` channel, `checkChannelHashcash` (`internal/service/service.go`) records the stamp before the membership, ban and `+m` checks in `SendChannelMessage`, so a message refused with 404 has spent a stamp on nothing. `README.md` (Why Hashcash) says a legitimate user "pays once"; the error tables say nothing about a refused request consuming the stamp. Verified by reading the order of calls; cost to the client, not corruption.
Fix: check the proof first and claim the stamp last. For session creation, split `Validate` into a check and a claim, claim just before `CreateSession` and release the claim if the insert fails; for channel messages, do the membership, ban and `+m` checks before the claim. With https://git.eeqj.de/sneak/neoirc/issues/150, which makes the claim the replay check, the claim is then the last step before the message is stored.
## Definition of done
1. A `POST /api/v1/session` refused with 400 or 409 leaves its stamp usable, and the same stamp then creates a session.
2. A `+H` PRIVMSG refused for membership, ban or `+m` leaves its stamp usable.
3. Tests for both.
4. One reviewed PR against `next`.
Model: fable-5-1
clawbot
self-assigned this 2026-10-20 17:14:53 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
handleCreateSessionininternal/handlers/api.gocallsvalidateHashcashfirst, andValidator.Validate(internal/hashcash/hashcash.go) records the stamp as spent as soon as it is valid. The nick check, the username check andCreateSession's unique-nick check come after. A request answered400invalid nick formator409nick already takenhas spent its stamp, and the same stamp retried gets402stamp already used: at the default 20 bits the user pays another half second to two seconds of proof-of-work for a nick collision. On a+Hchannel,checkChannelHashcash(internal/service/service.go) records the stamp before the membership, ban and+mchecks inSendChannelMessage, so a message refused with 404 has spent a stamp on nothing.README.md(Why Hashcash) says a legitimate user "pays once"; the error tables say nothing about a refused request consuming the stamp. Verified by reading the order of calls; cost to the client, not corruption.Fix: check the proof first and claim the stamp last. For session creation, split
Validateinto a check and a claim, claim just beforeCreateSessionand release the claim if the insert fails; for channel messages, do the membership, ban and+mchecks before the claim. With #150, which makes the claim the replay check, the claim is then the last step before the message is stored.Definition of done
POST /api/v1/sessionrefused with 400 or 409 leaves its stamp usable, and the same stamp then creates a session.+HPRIVMSG refused for membership, ban or+mleaves its stamp usable.next.Model: fable-5-1