@@ -585,6 +585,146 @@ for ordinary reads and listings.
585585[ Netlify Functions ] : https://docs.netlify.com/build/functions/overview/
586586
587587
588+ Bounding how long cache entries live
589+ ------------------------------------
590+
591+ * This section is relevant since Fedify 2.4.0.*
592+
593+ Fedify keeps two caches in your ` KvStore ` : cached actor public keys and
594+ remembered per-origin HTTP Message Signatures specs. Since Fedify 2.4.0
595+ both are written with a time-to-live, so a ` KvStore ` that never sees an
596+ explicit clear no longer accumulates entries for actors and origins that have
597+ stopped federating. The defaults are 30 days for cached keys and 90 days for
598+ remembered specs, and applications can override them through
599+ ` ~FederationOptions.publicKeyTtl ` and
600+ ` ~FederationOptions.httpMessageSignaturesSpecTtl ` :
601+
602+ ~~~~ typescript twoslash
603+ import { createFederation , MemoryKvStore } from " @fedify/fedify" ;
604+
605+ const federation = createFederation <void >({
606+ kv: new MemoryKvStore (),
607+ publicKeyTtl: { days: 7 }, // [!code highlight]
608+ httpMessageSignaturesSpecTtl: { days: 30 }, // [!code highlight]
609+ });
610+ ~~~~
611+
612+ Both TTLs are a retention tradeoff, not a free cleanup knob. A shorter TTL
613+ keeps less in the store and bounds how long a revoked or rotated key or an
614+ outdated spec stays cached, but every expiry costs a request to the remote
615+ server: verification has to refetch the key, and delivery has to relearn the
616+ spec by [ double-knocking] again. That refetch is not guaranteed to succeed—if
617+ the peer is down, unreachable, or has removed the actor when the entry expires,
618+ verification fails where it would have succeeded from cache. Lengthening
619+ a TTL inverts the tradeoff: fewer remote requests and more tolerance of
620+ unavailable peers, at the cost of holding stale entries longer.
621+
622+ Pick the shorter end when your store is under space pressure or you need
623+ revoked keys to fall out quickly, and the longer end when you federate with
624+ peers that are frequently unavailable.
625+
626+ [ double-knocking ] : https://swicg.github.io/activitypub-http-signature/#how-to-upgrade-supported-versions
627+
628+
629+ Clearing legacy cache entries
630+ -----------------------------
631+
632+ * This section is relevant since Fedify 2.4.0.*
633+
634+ Entries written by Fedify 2.3 or earlier have no TTL. They are * not*
635+ migrated or expired automatically: they simply stay in your ` KvStore ` until
636+ something overwrites them, which is the same behavior Fedify has always had.
637+ Leaving them alone is a perfectly valid choice—Fedify keeps serving and
638+ refreshing them as before, and they get a TTL the next time they are written.
639+
640+ If you would rather not wait for that, you can clear the old entries yourself.
641+ Both caches live under their ` ~FederationOptions.kvPrefixes ` entries, which
642+ are ` ["_fedify", "publicKey"] ` and
643+ ` ["_fedify", "httpMessageSignaturesSpec"] ` * by default* :
644+
645+ - ` ~FederationKvPrefixes.publicKey ` — cached actor public keys
646+ - ` ~FederationKvPrefixes.httpMessageSignaturesSpec ` — remembered HTTP
647+ Message Signatures specs
648+
649+ These are defaults, not fixed values. If you passed your own ` kvPrefixes ` to
650+ ` createFederation() ` , substitute your prefixes for ` _fedify ` , ` publicKey ` , and
651+ ` httpMessageSignaturesSpec ` in every example below. The same goes for the
652+ adapter-level namespacing described in each subsection: [ ` RedisKvStore ` ]
653+ prepends its own ` keyPrefix ` , and [ ` PostgresKvStore ` ] stores rows in its own
654+ ` tableName ` .
655+
656+ Clearing these entries costs the remote requests described in the previous
657+ section: the caches are soft state that Fedify relearns on demand, but every
658+ cleared key has to be refetched before it can be used again, and that refetch
659+ fails while the peer is unavailable. Prefer clearing them while your peers
660+ are reachable, and clear only the prefixes you actually need to reclaim.
661+
662+ ### Clearing entries in ` RedisKvStore `
663+
664+ [ ` RedisKvStore ` ] stores every key under a shared prefix (` "fedify::" ` by
665+ default, configurable via ` RedisKvStoreOptions.keyPrefix ` ), followed by the
666+ ` KvKey ` parts joined with ` "::" ` . Collect the whole scan result before
667+ deleting anything—deleting keys while ` --scan ` is still iterating can make
668+ the cursor skip entries:
669+
670+ ~~~~ bash
671+ for pattern in ' fedify::_fedify::publicKey::*' \
672+ ' fedify::_fedify::httpMessageSignaturesSpec::*' ; do
673+ redis-cli --scan --pattern " $pattern " > /tmp/fedify-keys.txt
674+ test -s /tmp/fedify-keys.txt && xargs -a /tmp/fedify-keys.txt redis-cli del
675+ rm -f /tmp/fedify-keys.txt
676+ done
677+ ~~~~
678+
679+ Replace the leading ` fedify:: ` with your own ` keyPrefix ` if you configured
680+ a custom one, and the ` _fedify::publicKey ` and
681+ ` _fedify::httpMessageSignaturesSpec ` parts with your own ` kvPrefixes ` .
682+
683+ ### Clearing entries in ` PostgresKvStore `
684+
685+ [ ` PostgresKvStore ` ] stores every entry as a row keyed by a ` text[] ` column
686+ (the table is named ` fedify_kv_v2 ` by default, configurable via
687+ ` PostgresKvStoreOptions.tableName ` ). Delete the two Fedify caches with:
688+
689+ ~~~~ sql
690+ DELETE FROM fedify_kv_v2
691+ WHERE array_length(key, 1 ) >= 2 AND key[1 :2 ] = ARRAY[' _fedify' , ' publicKey' ];
692+
693+ DELETE FROM fedify_kv_v2
694+ WHERE array_length(key, 1 ) >= 2
695+ AND key[1 :2 ] = ARRAY[' _fedify' , ' httpMessageSignaturesSpec' ];
696+ ~~~~
697+
698+ Replace ` fedify_kv_v2 ` with your own ` tableName ` if you configured a custom
699+ one, and the array literals with your own ` kvPrefixes ` .
700+
701+ ### Clearing entries in other ` KvStore ` implementations
702+
703+ For any other ` KvStore ` , iterate the two prefixes with [ ` ~KvStore.list() ` ] ,
704+ collect the keys, and delete them afterwards. Deleting while the iterator is
705+ still open can make an implementation skip entries, the same way it does with
706+ ` redis-cli --scan ` :
707+
708+ ~~~~ typescript twoslash
709+ import type { KvKey , KvStore } from " @fedify/fedify" ;
710+ const kv = null as unknown as KvStore ;
711+ // ---cut-before---
712+ const prefixes: KvKey [] = [
713+ [" _fedify" , " publicKey" ],
714+ [" _fedify" , " httpMessageSignaturesSpec" ],
715+ ];
716+ for (const prefix of prefixes ) {
717+ const keys: KvKey [] = [];
718+ for await (const entry of kv .list (prefix )) keys .push (entry .key );
719+ for (const key of keys ) await kv .delete (key );
720+ }
721+ ~~~~
722+
723+ Substitute your own ` kvPrefixes ` for the two prefixes if you configured them.
724+
725+ [ `~KvStore.list()` ] : https://jsr.io/@fedify/fedify/doc/federation/~/KvStore#list
726+
727+
588728Implementing a custom ` KvStore `
589729-------------------------------
590730
0 commit comments