Phase C cleanup follow-up to the routes/federation split (#9). Behavior- preserving; full server suite (790 tests) green. A) rateLimits.ts: the four near-identical sliding-window limiters (accept/relay/lookup/ensure) and their duplicated prune loops collapse into one createLimiter(windowMs, max) factory. Per-call and periodic- sweep semantics are preserved exactly, including that lookup buckets are pruned per-call but never swept (unchanged from before). 177 -> 101 lines. B) Extract sendSignedJson(reply, payload, hmacSecret) — the single definition of how this instance signs an S2S JSON response — and use it in the /epoch and /verify-attach-proof|reattach handlers, replacing two copies of the build-headers-and-send boilerplate.
25 lines
1.2 KiB
TypeScript
25 lines
1.2 KiB
TypeScript
import type { FastifyReply } from 'fastify';
|
|
import { buildFederationHeaders, getOurOrigin } from '../../../utils/federationAuth.js';
|
|
|
|
/**
|
|
* Serialize `payload` as JSON and send it as a `200` response signed with the
|
|
* peer's shared HMAC secret, so the receiving instance can verify authenticity
|
|
* (or trust a fail-closed verdict) of the body it carries.
|
|
*
|
|
* This is the single definition of how this instance signs S2S responses: the
|
|
* body is stringified once and the signature is computed over those exact bytes,
|
|
* which are the bytes sent (Content-Type is set explicitly so Fastify does not
|
|
* re-serialize and desync the signature).
|
|
*/
|
|
export function sendSignedJson(reply: FastifyReply, payload: unknown, hmacSecret: string): FastifyReply {
|
|
const responseBody = JSON.stringify(payload);
|
|
const sigHeaders = buildFederationHeaders(responseBody, hmacSecret, getOurOrigin());
|
|
reply.headers({
|
|
'X-Federation-Signature': sigHeaders['X-Federation-Signature'],
|
|
'X-Federation-Timestamp': sigHeaders['X-Federation-Timestamp'],
|
|
'X-Federation-Nonce': sigHeaders['X-Federation-Nonce'],
|
|
'Content-Type': 'application/json',
|
|
});
|
|
return reply.code(200).send(responseBody);
|
|
}
|