refactor: move hashcash stamp from X-Hashcash header to JSON request body
All checks were successful
check / check (push) Successful in 4s

Move the hashcash proof-of-work stamp from the X-Hashcash HTTP header
into the JSON request body as a 'hashcash' field on POST /api/v1/session.

Updated server handler, CLI client, SPA client, and documentation.
This commit is contained in:
clawbot
2026-03-10 10:00:20 -07:00
parent 8854b17ebc
commit 18d46b2ac0
5 changed files with 52 additions and 59 deletions

View File

@@ -52,7 +52,7 @@ func (client *Client) CreateSession(
// Fetch server info to check for hashcash requirement.
info, err := client.GetServerInfo()
var headers map[string]string
var hashcashStamp string
if err == nil && info.HashcashBits > 0 {
resource := info.Name
@@ -60,17 +60,13 @@ func (client *Client) CreateSession(
resource = "neoirc"
}
stamp := MintHashcash(info.HashcashBits, resource)
headers = map[string]string{
"X-Hashcash": stamp,
}
hashcashStamp = MintHashcash(info.HashcashBits, resource)
}
data, err := client.doWithHeaders(
data, err := client.do(
http.MethodPost,
"/api/v1/session",
&SessionRequest{Nick: nick},
headers,
&SessionRequest{Nick: nick, Hashcash: hashcashStamp},
)
if err != nil {
return nil, err

View File

@@ -4,7 +4,8 @@ import "time"
// SessionRequest is the body for POST /api/v1/session.
type SessionRequest struct {
Nick string `json:"nick"`
Nick string `json:"nick"`
Hashcash string `json:"hashcash,omitempty"`
}
// SessionResponse is the response from session creation.

View File

@@ -144,35 +144,9 @@ func (hdlr *Handlers) handleCreateSession(
writer http.ResponseWriter,
request *http.Request,
) {
// Validate hashcash proof-of-work if configured.
if hdlr.params.Config.HashcashBits > 0 {
stamp := request.Header.Get("X-Hashcash")
if stamp == "" {
hdlr.respondError(
writer, request,
"hashcash proof-of-work required",
http.StatusPaymentRequired,
)
return
}
err := hdlr.hashcashVal.Validate(
stamp, hdlr.params.Config.HashcashBits,
)
if err != nil {
hdlr.respondError(
writer, request,
"invalid hashcash stamp: "+err.Error(),
http.StatusPaymentRequired,
)
return
}
}
type createRequest struct {
Nick string `json:"nick"`
Nick string `json:"nick"`
Hashcash string `json:"hashcash,omitempty"`
}
var payload createRequest
@@ -188,6 +162,32 @@ func (hdlr *Handlers) handleCreateSession(
return
}
// Validate hashcash proof-of-work if configured.
if hdlr.params.Config.HashcashBits > 0 {
if payload.Hashcash == "" {
hdlr.respondError(
writer, request,
"hashcash proof-of-work required",
http.StatusPaymentRequired,
)
return
}
err = hdlr.hashcashVal.Validate(
payload.Hashcash, hdlr.params.Config.HashcashBits,
)
if err != nil {
hdlr.respondError(
writer, request,
"invalid hashcash stamp: "+err.Error(),
http.StatusPaymentRequired,
)
return
}
}
payload.Nick = strings.TrimSpace(payload.Nick)
if !validNickRe.MatchString(payload.Nick) {