@@ -104,8 +104,22 @@ export const actorDispatcher = (hostDataContextLoader: HostDataContextLoader) =>
104104export const keypairDispatcher = (
105105 accountService : AccountService ,
106106 hostDataContextLoader : HostDataContextLoader ,
107- ) =>
108- async function keypairDispatcher ( ctx : FedifyContext , identifier : string ) {
107+ ) => {
108+ const MAX_CACHE_SIZE = 1000 ;
109+ const KEY_TTL_MS = 30 * 60 * 1000 ; // 30 minutes
110+ const cryptoKeyCache = new Map <
111+ number ,
112+ {
113+ publicKey : CryptoKey ;
114+ privateKey : CryptoKey ;
115+ createdAt : number ;
116+ }
117+ > ( ) ;
118+
119+ return async function keypairDispatcher (
120+ ctx : FedifyContext ,
121+ identifier : string ,
122+ ) {
109123 const hostData = await hostDataContextLoader . loadDataForHost ( ctx . host ) ;
110124
111125 if ( isError ( hostData ) ) {
@@ -145,6 +159,14 @@ export const keypairDispatcher = (
145159
146160 const { account } = getValue ( hostData ) ;
147161
162+ const cached = cryptoKeyCache . get ( account . id ) ;
163+ if ( cached ) {
164+ if ( Date . now ( ) - cached . createdAt < KEY_TTL_MS ) {
165+ return [ cached ] ;
166+ }
167+ cryptoKeyCache . delete ( account . id ) ;
168+ }
169+
148170 const keyPair = await accountService . getKeyPair ( account . id ) ;
149171
150172 if ( isError ( keyPair ) ) {
@@ -176,18 +198,47 @@ export const keypairDispatcher = (
176198 const { publicKey, privateKey } = getValue ( keyPair ) ;
177199
178200 try {
179- return [
180- {
181- publicKey : await importJwk (
182- JSON . parse ( publicKey ) as JsonWebKey ,
183- 'public' ,
184- ) ,
185- privateKey : await importJwk (
186- JSON . parse ( privateKey ) as JsonWebKey ,
187- 'private' ,
188- ) ,
189- } ,
190- ] ;
201+ const imported = {
202+ publicKey : await importJwk (
203+ JSON . parse ( publicKey ) as JsonWebKey ,
204+ 'public' ,
205+ ) ,
206+ privateKey : await importJwk (
207+ JSON . parse ( privateKey ) as JsonWebKey ,
208+ 'private' ,
209+ ) ,
210+ } ;
211+
212+ const now = Date . now ( ) ;
213+
214+ // Evict expired entries
215+ for ( const [ key , entry ] of cryptoKeyCache ) {
216+ if ( now - entry . createdAt >= KEY_TTL_MS ) {
217+ cryptoKeyCache . delete ( key ) ;
218+ }
219+ }
220+
221+ // If still full, evict the oldest entry
222+ if ( cryptoKeyCache . size >= MAX_CACHE_SIZE ) {
223+ let oldestKey : number | undefined ;
224+ let oldestTime = Infinity ;
225+ for ( const [ key , entry ] of cryptoKeyCache ) {
226+ if ( entry . createdAt < oldestTime ) {
227+ oldestTime = entry . createdAt ;
228+ oldestKey = key ;
229+ }
230+ }
231+ if ( oldestKey !== undefined ) {
232+ cryptoKeyCache . delete ( oldestKey ) ;
233+ }
234+ }
235+
236+ cryptoKeyCache . set ( account . id , {
237+ ...imported ,
238+ createdAt : now ,
239+ } ) ;
240+
241+ return [ imported ] ;
191242 } catch ( error ) {
192243 ctx . data . logger . error (
193244 'Could not parse keypair for {host} (identifier: {identifier}): {error}' ,
@@ -200,6 +251,7 @@ export const keypairDispatcher = (
200251 return [ ] ;
201252 }
202253 } ;
254+ } ;
203255
204256export function createAcceptHandler ( accountService : AccountService ) {
205257 return async function handleAccept ( ctx : FedifyContext , accept : Accept ) {
0 commit comments