diff --git a/lib/pleroma/emoji-test.txt b/lib/pleroma/emoji-test.txt
index dd5493366..87d093d64 100644
--- a/lib/pleroma/emoji-test.txt
+++ b/lib/pleroma/emoji-test.txt
@@ -1,13 +1,13 @@
 # emoji-test.txt
-# Date: 2021-08-26, 17:22:23 GMT
-# ยฉ 2021 Unicodeยฎ, Inc.
+# Date: 2022-08-12, 20:24:39 GMT
+# ยฉ 2022 Unicodeยฎ, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
-# For terms of use, see http://www.unicode.org/terms_of_use.html
+# For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Emoji Keyboard/Display Test Data for UTS #51
-# Version: 14.0
+# Version: 15.0
 #
-# For documentation and usage, see http://www.unicode.org/reports/tr51
+# For documentation and usage, see https://www.unicode.org/reports/tr51
 #
 # This file provides data for testing which emoji forms should be in keyboards and which should also be displayed/processed.
 # Format: code points; status # emoji name
@@ -92,6 +92,7 @@
 1F62C                                                  ; fully-qualified     # ๐Ÿ˜ฌ E1.0 grimacing face
 1F62E 200D 1F4A8                                       ; fully-qualified     # ๐Ÿ˜ฎโ€๐Ÿ’จ E13.1 face exhaling
 1F925                                                  ; fully-qualified     # ๐Ÿคฅ E3.0 lying face
+1FAE8                                                  ; fully-qualified     # ๐Ÿซจ E15.0 shaking face
 
 # subgroup: face-sleepy
 1F60C                                                  ; fully-qualified     # ๐Ÿ˜Œ E0.6 relieved face
@@ -155,7 +156,7 @@
 
 # subgroup: face-negative
 1F624                                                  ; fully-qualified     # ๐Ÿ˜ค E0.6 face with steam from nose
-1F621                                                  ; fully-qualified     # ๐Ÿ˜ก E0.6 pouting face
+1F621                                                  ; fully-qualified     # ๐Ÿ˜ก E0.6 enraged face
 1F620                                                  ; fully-qualified     # ๐Ÿ˜  E0.6 angry face
 1F92C                                                  ; fully-qualified     # ๐Ÿคฌ E5.0 face with symbols on mouth
 1F608                                                  ; fully-qualified     # ๐Ÿ˜ˆ E1.0 smiling face with horns
@@ -190,8 +191,7 @@
 1F649                                                  ; fully-qualified     # ๐Ÿ™‰ E0.6 hear-no-evil monkey
 1F64A                                                  ; fully-qualified     # ๐Ÿ™Š E0.6 speak-no-evil monkey
 
-# subgroup: emotion
-1F48B                                                  ; fully-qualified     # ๐Ÿ’‹ E0.6 kiss mark
+# subgroup: heart
 1F48C                                                  ; fully-qualified     # ๐Ÿ’Œ E0.6 love letter
 1F498                                                  ; fully-qualified     # ๐Ÿ’˜ E0.6 heart with arrow
 1F49D                                                  ; fully-qualified     # ๐Ÿ’ E0.6 heart with ribbon
@@ -210,14 +210,20 @@
 2764 200D 1FA79                                        ; unqualified         # โคโ€๐Ÿฉน E13.1 mending heart
 2764 FE0F                                              ; fully-qualified     # โค๏ธ E0.6 red heart
 2764                                                   ; unqualified         # โค E0.6 red heart
+1FA77                                                  ; fully-qualified     # ๐Ÿฉท E15.0 pink heart
 1F9E1                                                  ; fully-qualified     # ๐Ÿงก E5.0 orange heart
 1F49B                                                  ; fully-qualified     # ๐Ÿ’› E0.6 yellow heart
 1F49A                                                  ; fully-qualified     # ๐Ÿ’š E0.6 green heart
 1F499                                                  ; fully-qualified     # ๐Ÿ’™ E0.6 blue heart
+1FA75                                                  ; fully-qualified     # ๐Ÿฉต E15.0 light blue heart
 1F49C                                                  ; fully-qualified     # ๐Ÿ’œ E0.6 purple heart
 1F90E                                                  ; fully-qualified     # ๐ŸคŽ E12.0 brown heart
 1F5A4                                                  ; fully-qualified     # ๐Ÿ–ค E3.0 black heart
+1FA76                                                  ; fully-qualified     # ๐Ÿฉถ E15.0 grey heart
 1F90D                                                  ; fully-qualified     # ๐Ÿค E12.0 white heart
+
+# subgroup: emotion
+1F48B                                                  ; fully-qualified     # ๐Ÿ’‹ E0.6 kiss mark
 1F4AF                                                  ; fully-qualified     # ๐Ÿ’ฏ E0.6 hundred points
 1F4A2                                                  ; fully-qualified     # ๐Ÿ’ข E0.6 anger symbol
 1F4A5                                                  ; fully-qualified     # ๐Ÿ’ฅ E0.6 collision
@@ -226,21 +232,20 @@
 1F4A8                                                  ; fully-qualified     # ๐Ÿ’จ E0.6 dashing away
 1F573 FE0F                                             ; fully-qualified     # ๐Ÿ•ณ๏ธ E0.7 hole
 1F573                                                  ; unqualified         # ๐Ÿ•ณ E0.7 hole
-1F4A3                                                  ; fully-qualified     # ๐Ÿ’ฃ E0.6 bomb
 1F4AC                                                  ; fully-qualified     # ๐Ÿ’ฌ E0.6 speech balloon
 1F441 FE0F 200D 1F5E8 FE0F                             ; fully-qualified     # ๐Ÿ‘๏ธโ€๐Ÿ—จ๏ธ E2.0 eye in speech bubble
 1F441 200D 1F5E8 FE0F                                  ; unqualified         # ๐Ÿ‘โ€๐Ÿ—จ๏ธ E2.0 eye in speech bubble
-1F441 FE0F 200D 1F5E8                                  ; unqualified         # ๐Ÿ‘๏ธโ€๐Ÿ—จ E2.0 eye in speech bubble
+1F441 FE0F 200D 1F5E8                                  ; minimally-qualified # ๐Ÿ‘๏ธโ€๐Ÿ—จ E2.0 eye in speech bubble
 1F441 200D 1F5E8                                       ; unqualified         # ๐Ÿ‘โ€๐Ÿ—จ E2.0 eye in speech bubble
 1F5E8 FE0F                                             ; fully-qualified     # ๐Ÿ—จ๏ธ E2.0 left speech bubble
 1F5E8                                                  ; unqualified         # ๐Ÿ—จ E2.0 left speech bubble
 1F5EF FE0F                                             ; fully-qualified     # ๐Ÿ—ฏ๏ธ E0.7 right anger bubble
 1F5EF                                                  ; unqualified         # ๐Ÿ—ฏ E0.7 right anger bubble
 1F4AD                                                  ; fully-qualified     # ๐Ÿ’ญ E1.0 thought balloon
-1F4A4                                                  ; fully-qualified     # ๐Ÿ’ค E0.6 zzz
+1F4A4                                                  ; fully-qualified     # ๐Ÿ’ค E0.6 ZZZ
 
-# Smileys & Emotion subtotal:		177
-# Smileys & Emotion subtotal:		177	w/o modifiers
+# Smileys & Emotion subtotal:		180
+# Smileys & Emotion subtotal:		180	w/o modifiers
 
 # group: People & Body
 
@@ -300,6 +305,18 @@
 1FAF4 1F3FD                                            ; fully-qualified     # ๐Ÿซด๐Ÿฝ E14.0 palm up hand: medium skin tone
 1FAF4 1F3FE                                            ; fully-qualified     # ๐Ÿซด๐Ÿพ E14.0 palm up hand: medium-dark skin tone
 1FAF4 1F3FF                                            ; fully-qualified     # ๐Ÿซด๐Ÿฟ E14.0 palm up hand: dark skin tone
+1FAF7                                                  ; fully-qualified     # ๐Ÿซท E15.0 leftwards pushing hand
+1FAF7 1F3FB                                            ; fully-qualified     # ๐Ÿซท๐Ÿป E15.0 leftwards pushing hand: light skin tone
+1FAF7 1F3FC                                            ; fully-qualified     # ๐Ÿซท๐Ÿผ E15.0 leftwards pushing hand: medium-light skin tone
+1FAF7 1F3FD                                            ; fully-qualified     # ๐Ÿซท๐Ÿฝ E15.0 leftwards pushing hand: medium skin tone
+1FAF7 1F3FE                                            ; fully-qualified     # ๐Ÿซท๐Ÿพ E15.0 leftwards pushing hand: medium-dark skin tone
+1FAF7 1F3FF                                            ; fully-qualified     # ๐Ÿซท๐Ÿฟ E15.0 leftwards pushing hand: dark skin tone
+1FAF8                                                  ; fully-qualified     # ๐Ÿซธ E15.0 rightwards pushing hand
+1FAF8 1F3FB                                            ; fully-qualified     # ๐Ÿซธ๐Ÿป E15.0 rightwards pushing hand: light skin tone
+1FAF8 1F3FC                                            ; fully-qualified     # ๐Ÿซธ๐Ÿผ E15.0 rightwards pushing hand: medium-light skin tone
+1FAF8 1F3FD                                            ; fully-qualified     # ๐Ÿซธ๐Ÿฝ E15.0 rightwards pushing hand: medium skin tone
+1FAF8 1F3FE                                            ; fully-qualified     # ๐Ÿซธ๐Ÿพ E15.0 rightwards pushing hand: medium-dark skin tone
+1FAF8 1F3FF                                            ; fully-qualified     # ๐Ÿซธ๐Ÿฟ E15.0 rightwards pushing hand: dark skin tone
 
 # subgroup: hand-fingers-partial
 1F44C                                                  ; fully-qualified     # ๐Ÿ‘Œ E0.6 OK hand
@@ -473,11 +490,11 @@
 1F932 1F3FE                                            ; fully-qualified     # ๐Ÿคฒ๐Ÿพ E5.0 palms up together: medium-dark skin tone
 1F932 1F3FF                                            ; fully-qualified     # ๐Ÿคฒ๐Ÿฟ E5.0 palms up together: dark skin tone
 1F91D                                                  ; fully-qualified     # ๐Ÿค E3.0 handshake
-1F91D 1F3FB                                            ; fully-qualified     # ๐Ÿค๐Ÿป E3.0 handshake: light skin tone
-1F91D 1F3FC                                            ; fully-qualified     # ๐Ÿค๐Ÿผ E3.0 handshake: medium-light skin tone
-1F91D 1F3FD                                            ; fully-qualified     # ๐Ÿค๐Ÿฝ E3.0 handshake: medium skin tone
-1F91D 1F3FE                                            ; fully-qualified     # ๐Ÿค๐Ÿพ E3.0 handshake: medium-dark skin tone
-1F91D 1F3FF                                            ; fully-qualified     # ๐Ÿค๐Ÿฟ E3.0 handshake: dark skin tone
+1F91D 1F3FB                                            ; fully-qualified     # ๐Ÿค๐Ÿป E14.0 handshake: light skin tone
+1F91D 1F3FC                                            ; fully-qualified     # ๐Ÿค๐Ÿผ E14.0 handshake: medium-light skin tone
+1F91D 1F3FD                                            ; fully-qualified     # ๐Ÿค๐Ÿฝ E14.0 handshake: medium skin tone
+1F91D 1F3FE                                            ; fully-qualified     # ๐Ÿค๐Ÿพ E14.0 handshake: medium-dark skin tone
+1F91D 1F3FF                                            ; fully-qualified     # ๐Ÿค๐Ÿฟ E14.0 handshake: dark skin tone
 1FAF1 1F3FB 200D 1FAF2 1F3FC                           ; fully-qualified     # ๐Ÿซฑ๐Ÿปโ€๐Ÿซฒ๐Ÿผ E14.0 handshake: light skin tone, medium-light skin tone
 1FAF1 1F3FB 200D 1FAF2 1F3FD                           ; fully-qualified     # ๐Ÿซฑ๐Ÿปโ€๐Ÿซฒ๐Ÿฝ E14.0 handshake: light skin tone, medium skin tone
 1FAF1 1F3FB 200D 1FAF2 1F3FE                           ; fully-qualified     # ๐Ÿซฑ๐Ÿปโ€๐Ÿซฒ๐Ÿพ E14.0 handshake: light skin tone, medium-dark skin tone
@@ -1455,7 +1472,7 @@
 1F575 1F3FF                                            ; fully-qualified     # ๐Ÿ•ต๐Ÿฟ E2.0 detective: dark skin tone
 1F575 FE0F 200D 2642 FE0F                              ; fully-qualified     # ๐Ÿ•ต๏ธโ€โ™‚๏ธ E4.0 man detective
 1F575 200D 2642 FE0F                                   ; unqualified         # ๐Ÿ•ตโ€โ™‚๏ธ E4.0 man detective
-1F575 FE0F 200D 2642                                   ; unqualified         # ๐Ÿ•ต๏ธโ€โ™‚ E4.0 man detective
+1F575 FE0F 200D 2642                                   ; minimally-qualified # ๐Ÿ•ต๏ธโ€โ™‚ E4.0 man detective
 1F575 200D 2642                                        ; unqualified         # ๐Ÿ•ตโ€โ™‚ E4.0 man detective
 1F575 1F3FB 200D 2642 FE0F                             ; fully-qualified     # ๐Ÿ•ต๐Ÿปโ€โ™‚๏ธ E4.0 man detective: light skin tone
 1F575 1F3FB 200D 2642                                  ; minimally-qualified # ๐Ÿ•ต๐Ÿปโ€โ™‚ E4.0 man detective: light skin tone
@@ -1469,7 +1486,7 @@
 1F575 1F3FF 200D 2642                                  ; minimally-qualified # ๐Ÿ•ต๐Ÿฟโ€โ™‚ E4.0 man detective: dark skin tone
 1F575 FE0F 200D 2640 FE0F                              ; fully-qualified     # ๐Ÿ•ต๏ธโ€โ™€๏ธ E4.0 woman detective
 1F575 200D 2640 FE0F                                   ; unqualified         # ๐Ÿ•ตโ€โ™€๏ธ E4.0 woman detective
-1F575 FE0F 200D 2640                                   ; unqualified         # ๐Ÿ•ต๏ธโ€โ™€ E4.0 woman detective
+1F575 FE0F 200D 2640                                   ; minimally-qualified # ๐Ÿ•ต๏ธโ€โ™€ E4.0 woman detective
 1F575 200D 2640                                        ; unqualified         # ๐Ÿ•ตโ€โ™€ E4.0 woman detective
 1F575 1F3FB 200D 2640 FE0F                             ; fully-qualified     # ๐Ÿ•ต๐Ÿปโ€โ™€๏ธ E4.0 woman detective: light skin tone
 1F575 1F3FB 200D 2640                                  ; minimally-qualified # ๐Ÿ•ต๐Ÿปโ€โ™€ E4.0 woman detective: light skin tone
@@ -2302,7 +2319,7 @@
 1F3CC 1F3FF                                            ; fully-qualified     # ๐ŸŒ๐Ÿฟ E4.0 person golfing: dark skin tone
 1F3CC FE0F 200D 2642 FE0F                              ; fully-qualified     # ๐ŸŒ๏ธโ€โ™‚๏ธ E4.0 man golfing
 1F3CC 200D 2642 FE0F                                   ; unqualified         # ๐ŸŒโ€โ™‚๏ธ E4.0 man golfing
-1F3CC FE0F 200D 2642                                   ; unqualified         # ๐ŸŒ๏ธโ€โ™‚ E4.0 man golfing
+1F3CC FE0F 200D 2642                                   ; minimally-qualified # ๐ŸŒ๏ธโ€โ™‚ E4.0 man golfing
 1F3CC 200D 2642                                        ; unqualified         # ๐ŸŒโ€โ™‚ E4.0 man golfing
 1F3CC 1F3FB 200D 2642 FE0F                             ; fully-qualified     # ๐ŸŒ๐Ÿปโ€โ™‚๏ธ E4.0 man golfing: light skin tone
 1F3CC 1F3FB 200D 2642                                  ; minimally-qualified # ๐ŸŒ๐Ÿปโ€โ™‚ E4.0 man golfing: light skin tone
@@ -2316,7 +2333,7 @@
 1F3CC 1F3FF 200D 2642                                  ; minimally-qualified # ๐ŸŒ๐Ÿฟโ€โ™‚ E4.0 man golfing: dark skin tone
 1F3CC FE0F 200D 2640 FE0F                              ; fully-qualified     # ๐ŸŒ๏ธโ€โ™€๏ธ E4.0 woman golfing
 1F3CC 200D 2640 FE0F                                   ; unqualified         # ๐ŸŒโ€โ™€๏ธ E4.0 woman golfing
-1F3CC FE0F 200D 2640                                   ; unqualified         # ๐ŸŒ๏ธโ€โ™€ E4.0 woman golfing
+1F3CC FE0F 200D 2640                                   ; minimally-qualified # ๐ŸŒ๏ธโ€โ™€ E4.0 woman golfing
 1F3CC 200D 2640                                        ; unqualified         # ๐ŸŒโ€โ™€ E4.0 woman golfing
 1F3CC 1F3FB 200D 2640 FE0F                             ; fully-qualified     # ๐ŸŒ๐Ÿปโ€โ™€๏ธ E4.0 woman golfing: light skin tone
 1F3CC 1F3FB 200D 2640                                  ; minimally-qualified # ๐ŸŒ๐Ÿปโ€โ™€ E4.0 woman golfing: light skin tone
@@ -2427,7 +2444,7 @@
 26F9 1F3FF                                             ; fully-qualified     # โ›น๐Ÿฟ E2.0 person bouncing ball: dark skin tone
 26F9 FE0F 200D 2642 FE0F                               ; fully-qualified     # โ›น๏ธโ€โ™‚๏ธ E4.0 man bouncing ball
 26F9 200D 2642 FE0F                                    ; unqualified         # โ›นโ€โ™‚๏ธ E4.0 man bouncing ball
-26F9 FE0F 200D 2642                                    ; unqualified         # โ›น๏ธโ€โ™‚ E4.0 man bouncing ball
+26F9 FE0F 200D 2642                                    ; minimally-qualified # โ›น๏ธโ€โ™‚ E4.0 man bouncing ball
 26F9 200D 2642                                         ; unqualified         # โ›นโ€โ™‚ E4.0 man bouncing ball
 26F9 1F3FB 200D 2642 FE0F                              ; fully-qualified     # โ›น๐Ÿปโ€โ™‚๏ธ E4.0 man bouncing ball: light skin tone
 26F9 1F3FB 200D 2642                                   ; minimally-qualified # โ›น๐Ÿปโ€โ™‚ E4.0 man bouncing ball: light skin tone
@@ -2441,7 +2458,7 @@
 26F9 1F3FF 200D 2642                                   ; minimally-qualified # โ›น๐Ÿฟโ€โ™‚ E4.0 man bouncing ball: dark skin tone
 26F9 FE0F 200D 2640 FE0F                               ; fully-qualified     # โ›น๏ธโ€โ™€๏ธ E4.0 woman bouncing ball
 26F9 200D 2640 FE0F                                    ; unqualified         # โ›นโ€โ™€๏ธ E4.0 woman bouncing ball
-26F9 FE0F 200D 2640                                    ; unqualified         # โ›น๏ธโ€โ™€ E4.0 woman bouncing ball
+26F9 FE0F 200D 2640                                    ; minimally-qualified # โ›น๏ธโ€โ™€ E4.0 woman bouncing ball
 26F9 200D 2640                                         ; unqualified         # โ›นโ€โ™€ E4.0 woman bouncing ball
 26F9 1F3FB 200D 2640 FE0F                              ; fully-qualified     # โ›น๐Ÿปโ€โ™€๏ธ E4.0 woman bouncing ball: light skin tone
 26F9 1F3FB 200D 2640                                   ; minimally-qualified # โ›น๐Ÿปโ€โ™€ E4.0 woman bouncing ball: light skin tone
@@ -2462,7 +2479,7 @@
 1F3CB 1F3FF                                            ; fully-qualified     # ๐Ÿ‹๐Ÿฟ E2.0 person lifting weights: dark skin tone
 1F3CB FE0F 200D 2642 FE0F                              ; fully-qualified     # ๐Ÿ‹๏ธโ€โ™‚๏ธ E4.0 man lifting weights
 1F3CB 200D 2642 FE0F                                   ; unqualified         # ๐Ÿ‹โ€โ™‚๏ธ E4.0 man lifting weights
-1F3CB FE0F 200D 2642                                   ; unqualified         # ๐Ÿ‹๏ธโ€โ™‚ E4.0 man lifting weights
+1F3CB FE0F 200D 2642                                   ; minimally-qualified # ๐Ÿ‹๏ธโ€โ™‚ E4.0 man lifting weights
 1F3CB 200D 2642                                        ; unqualified         # ๐Ÿ‹โ€โ™‚ E4.0 man lifting weights
 1F3CB 1F3FB 200D 2642 FE0F                             ; fully-qualified     # ๐Ÿ‹๐Ÿปโ€โ™‚๏ธ E4.0 man lifting weights: light skin tone
 1F3CB 1F3FB 200D 2642                                  ; minimally-qualified # ๐Ÿ‹๐Ÿปโ€โ™‚ E4.0 man lifting weights: light skin tone
@@ -2476,7 +2493,7 @@
 1F3CB 1F3FF 200D 2642                                  ; minimally-qualified # ๐Ÿ‹๐Ÿฟโ€โ™‚ E4.0 man lifting weights: dark skin tone
 1F3CB FE0F 200D 2640 FE0F                              ; fully-qualified     # ๐Ÿ‹๏ธโ€โ™€๏ธ E4.0 woman lifting weights
 1F3CB 200D 2640 FE0F                                   ; unqualified         # ๐Ÿ‹โ€โ™€๏ธ E4.0 woman lifting weights
-1F3CB FE0F 200D 2640                                   ; unqualified         # ๐Ÿ‹๏ธโ€โ™€ E4.0 woman lifting weights
+1F3CB FE0F 200D 2640                                   ; minimally-qualified # ๐Ÿ‹๏ธโ€โ™€ E4.0 woman lifting weights
 1F3CB 200D 2640                                        ; unqualified         # ๐Ÿ‹โ€โ™€ E4.0 woman lifting weights
 1F3CB 1F3FB 200D 2640 FE0F                             ; fully-qualified     # ๐Ÿ‹๐Ÿปโ€โ™€๏ธ E4.0 woman lifting weights: light skin tone
 1F3CB 1F3FB 200D 2640                                  ; minimally-qualified # ๐Ÿ‹๐Ÿปโ€โ™€ E4.0 woman lifting weights: light skin tone
@@ -3262,8 +3279,8 @@
 1FAC2                                                  ; fully-qualified     # ๐Ÿซ‚ E13.0 people hugging
 1F463                                                  ; fully-qualified     # ๐Ÿ‘ฃ E0.6 footprints
 
-# People & Body subtotal:		2986
-# People & Body subtotal:		506	w/o modifiers
+# People & Body subtotal:		2998
+# People & Body subtotal:		508	w/o modifiers
 
 # group: Component
 
@@ -3306,6 +3323,8 @@
 1F405                                                  ; fully-qualified     # ๐Ÿ… E1.0 tiger
 1F406                                                  ; fully-qualified     # ๐Ÿ† E1.0 leopard
 1F434                                                  ; fully-qualified     # ๐Ÿด E0.6 horse face
+1FACE                                                  ; fully-qualified     # ๐ŸซŽ E15.0 moose
+1FACF                                                  ; fully-qualified     # ๐Ÿซ E15.0 donkey
 1F40E                                                  ; fully-qualified     # ๐ŸŽ E0.6 horse
 1F984                                                  ; fully-qualified     # ๐Ÿฆ„ E1.0 unicorn
 1F993                                                  ; fully-qualified     # ๐Ÿฆ“ E5.0 zebra
@@ -3373,6 +3392,9 @@
 1F9A9                                                  ; fully-qualified     # ๐Ÿฆฉ E12.0 flamingo
 1F99A                                                  ; fully-qualified     # ๐Ÿฆš E11.0 peacock
 1F99C                                                  ; fully-qualified     # ๐Ÿฆœ E11.0 parrot
+1FABD                                                  ; fully-qualified     # ๐Ÿชฝ E15.0 wing
+1F426 200D 2B1B                                        ; fully-qualified     # ๐Ÿฆโ€โฌ› E15.0 black bird
+1FABF                                                  ; fully-qualified     # ๐Ÿชฟ E15.0 goose
 
 # subgroup: animal-amphibian
 1F438                                                  ; fully-qualified     # ๐Ÿธ E0.6 frog
@@ -3399,6 +3421,7 @@
 1F419                                                  ; fully-qualified     # ๐Ÿ™ E0.6 octopus
 1F41A                                                  ; fully-qualified     # ๐Ÿš E0.6 spiral shell
 1FAB8                                                  ; fully-qualified     # ๐Ÿชธ E14.0 coral
+1FABC                                                  ; fully-qualified     # ๐Ÿชผ E15.0 jellyfish
 
 # subgroup: animal-bug
 1F40C                                                  ; fully-qualified     # ๐ŸŒ E0.6 snail
@@ -3433,6 +3456,7 @@
 1F33B                                                  ; fully-qualified     # ๐ŸŒป E0.6 sunflower
 1F33C                                                  ; fully-qualified     # ๐ŸŒผ E0.6 blossom
 1F337                                                  ; fully-qualified     # ๐ŸŒท E0.6 tulip
+1FABB                                                  ; fully-qualified     # ๐Ÿชป E15.0 hyacinth
 
 # subgroup: plant-other
 1F331                                                  ; fully-qualified     # ๐ŸŒฑ E0.6 seedling
@@ -3451,9 +3475,10 @@
 1F343                                                  ; fully-qualified     # ๐Ÿƒ E0.6 leaf fluttering in wind
 1FAB9                                                  ; fully-qualified     # ๐Ÿชน E14.0 empty nest
 1FABA                                                  ; fully-qualified     # ๐Ÿชบ E14.0 nest with eggs
+1F344                                                  ; fully-qualified     # ๐Ÿ„ E0.6 mushroom
 
-# Animals & Nature subtotal:		151
-# Animals & Nature subtotal:		151	w/o modifiers
+# Animals & Nature subtotal:		159
+# Animals & Nature subtotal:		159	w/o modifiers
 
 # group: Food & Drink
 
@@ -3492,10 +3517,11 @@
 1F966                                                  ; fully-qualified     # ๐Ÿฅฆ E5.0 broccoli
 1F9C4                                                  ; fully-qualified     # ๐Ÿง„ E12.0 garlic
 1F9C5                                                  ; fully-qualified     # ๐Ÿง… E12.0 onion
-1F344                                                  ; fully-qualified     # ๐Ÿ„ E0.6 mushroom
 1F95C                                                  ; fully-qualified     # ๐Ÿฅœ E3.0 peanuts
 1FAD8                                                  ; fully-qualified     # ๐Ÿซ˜ E14.0 beans
 1F330                                                  ; fully-qualified     # ๐ŸŒฐ E0.6 chestnut
+1FADA                                                  ; fully-qualified     # ๐Ÿซš E15.0 ginger root
+1FADB                                                  ; fully-qualified     # ๐Ÿซ› E15.0 pea pod
 
 # subgroup: food-prepared
 1F35E                                                  ; fully-qualified     # ๐Ÿž E0.6 bread
@@ -3607,8 +3633,8 @@
 1FAD9                                                  ; fully-qualified     # ๐Ÿซ™ E14.0 jar
 1F3FA                                                  ; fully-qualified     # ๐Ÿบ E1.0 amphora
 
-# Food & Drink subtotal:		134
-# Food & Drink subtotal:		134	w/o modifiers
+# Food & Drink subtotal:		135
+# Food & Drink subtotal:		135	w/o modifiers
 
 # group: Travel & Places
 
@@ -3974,11 +4000,10 @@
 1F3AF                                                  ; fully-qualified     # ๐ŸŽฏ E0.6 bullseye
 1FA80                                                  ; fully-qualified     # ๐Ÿช€ E12.0 yo-yo
 1FA81                                                  ; fully-qualified     # ๐Ÿช E12.0 kite
+1F52B                                                  ; fully-qualified     # ๐Ÿ”ซ E0.6 water pistol
 1F3B1                                                  ; fully-qualified     # ๐ŸŽฑ E0.6 pool 8 ball
 1F52E                                                  ; fully-qualified     # ๐Ÿ”ฎ E0.6 crystal ball
 1FA84                                                  ; fully-qualified     # ๐Ÿช„ E13.0 magic wand
-1F9FF                                                  ; fully-qualified     # ๐Ÿงฟ E11.0 nazar amulet
-1FAAC                                                  ; fully-qualified     # ๐Ÿชฌ E14.0 hamsa
 1F3AE                                                  ; fully-qualified     # ๐ŸŽฎ E0.6 video game
 1F579 FE0F                                             ; fully-qualified     # ๐Ÿ•น๏ธ E0.7 joystick
 1F579                                                  ; unqualified         # ๐Ÿ•น E0.7 joystick
@@ -4013,8 +4038,8 @@
 1F9F6                                                  ; fully-qualified     # ๐Ÿงถ E11.0 yarn
 1FAA2                                                  ; fully-qualified     # ๐Ÿชข E13.0 knot
 
-# Activities subtotal:		97
-# Activities subtotal:		97	w/o modifiers
+# Activities subtotal:		96
+# Activities subtotal:		96	w/o modifiers
 
 # group: Objects
 
@@ -4040,6 +4065,7 @@
 1FA73                                                  ; fully-qualified     # ๐Ÿฉณ E12.0 shorts
 1F459                                                  ; fully-qualified     # ๐Ÿ‘™ E0.6 bikini
 1F45A                                                  ; fully-qualified     # ๐Ÿ‘š E0.6 womanโ€™s clothes
+1FAAD                                                  ; fully-qualified     # ๐Ÿชญ E15.0 folding hand fan
 1F45B                                                  ; fully-qualified     # ๐Ÿ‘› E0.6 purse
 1F45C                                                  ; fully-qualified     # ๐Ÿ‘œ E0.6 handbag
 1F45D                                                  ; fully-qualified     # ๐Ÿ‘ E0.6 clutch bag
@@ -4055,6 +4081,7 @@
 1F461                                                  ; fully-qualified     # ๐Ÿ‘ก E0.6 womanโ€™s sandal
 1FA70                                                  ; fully-qualified     # ๐Ÿฉฐ E12.0 ballet shoes
 1F462                                                  ; fully-qualified     # ๐Ÿ‘ข E0.6 womanโ€™s boot
+1FAAE                                                  ; fully-qualified     # ๐Ÿชฎ E15.0 hair pick
 1F451                                                  ; fully-qualified     # ๐Ÿ‘‘ E0.6 crown
 1F452                                                  ; fully-qualified     # ๐Ÿ‘’ E0.6 womanโ€™s hat
 1F3A9                                                  ; fully-qualified     # ๐ŸŽฉ E0.6 top hat
@@ -4103,6 +4130,8 @@
 1FA95                                                  ; fully-qualified     # ๐Ÿช• E12.0 banjo
 1F941                                                  ; fully-qualified     # ๐Ÿฅ E3.0 drum
 1FA98                                                  ; fully-qualified     # ๐Ÿช˜ E13.0 long drum
+1FA87                                                  ; fully-qualified     # ๐Ÿช‡ E15.0 maracas
+1FA88                                                  ; fully-qualified     # ๐Ÿชˆ E15.0 flute
 
 # subgroup: phone
 1F4F1                                                  ; fully-qualified     # ๐Ÿ“ฑ E0.6 mobile phone
@@ -4275,7 +4304,7 @@
 1F5E1                                                  ; unqualified         # ๐Ÿ—ก E0.7 dagger
 2694 FE0F                                              ; fully-qualified     # โš”๏ธ E1.0 crossed swords
 2694                                                   ; unqualified         # โš” E1.0 crossed swords
-1F52B                                                  ; fully-qualified     # ๐Ÿ”ซ E0.6 water pistol
+1F4A3                                                  ; fully-qualified     # ๐Ÿ’ฃ E0.6 bomb
 1FA83                                                  ; fully-qualified     # ๐Ÿชƒ E13.0 boomerang
 1F3F9                                                  ; fully-qualified     # ๐Ÿน E1.0 bow and arrow
 1F6E1 FE0F                                             ; fully-qualified     # ๐Ÿ›ก๏ธ E0.7 shield
@@ -4354,12 +4383,14 @@
 1FAA6                                                  ; fully-qualified     # ๐Ÿชฆ E13.0 headstone
 26B1 FE0F                                              ; fully-qualified     # โšฑ๏ธ E1.0 funeral urn
 26B1                                                   ; unqualified         # โšฑ E1.0 funeral urn
+1F9FF                                                  ; fully-qualified     # ๐Ÿงฟ E11.0 nazar amulet
+1FAAC                                                  ; fully-qualified     # ๐Ÿชฌ E14.0 hamsa
 1F5FF                                                  ; fully-qualified     # ๐Ÿ—ฟ E0.6 moai
 1FAA7                                                  ; fully-qualified     # ๐Ÿชง E13.0 placard
 1FAAA                                                  ; fully-qualified     # ๐Ÿชช E14.0 identification card
 
-# Objects subtotal:		304
-# Objects subtotal:		304	w/o modifiers
+# Objects subtotal:		310
+# Objects subtotal:		310	w/o modifiers
 
 # group: Symbols
 
@@ -4455,6 +4486,7 @@
 262E                                                   ; unqualified         # โ˜ฎ E1.0 peace symbol
 1F54E                                                  ; fully-qualified     # ๐Ÿ•Ž E1.0 menorah
 1F52F                                                  ; fully-qualified     # ๐Ÿ”ฏ E0.6 dotted six-pointed star
+1FAAF                                                  ; fully-qualified     # ๐Ÿชฏ E15.0 khanda
 
 # subgroup: zodiac
 2648                                                   ; fully-qualified     # โ™ˆ E0.6 Aries
@@ -4503,6 +4535,7 @@
 1F505                                                  ; fully-qualified     # ๐Ÿ”… E1.0 dim button
 1F506                                                  ; fully-qualified     # ๐Ÿ”† E1.0 bright button
 1F4F6                                                  ; fully-qualified     # ๐Ÿ“ถ E0.6 antenna bars
+1F6DC                                                  ; fully-qualified     # ๐Ÿ›œ E15.0 wireless
 1F4F3                                                  ; fully-qualified     # ๐Ÿ“ณ E0.6 vibration mode
 1F4F4                                                  ; fully-qualified     # ๐Ÿ“ด E0.6 mobile phone off
 
@@ -4693,8 +4726,8 @@
 1F533                                                  ; fully-qualified     # ๐Ÿ”ณ E0.6 white square button
 1F532                                                  ; fully-qualified     # ๐Ÿ”ฒ E0.6 black square button
 
-# Symbols subtotal:		302
-# Symbols subtotal:		302	w/o modifiers
+# Symbols subtotal:		304
+# Symbols subtotal:		304	w/o modifiers
 
 # group: Flags
 
@@ -4709,7 +4742,7 @@
 1F3F3 200D 1F308                                       ; unqualified         # ๐Ÿณโ€๐ŸŒˆ E4.0 rainbow flag
 1F3F3 FE0F 200D 26A7 FE0F                              ; fully-qualified     # ๐Ÿณ๏ธโ€โšง๏ธ E13.0 transgender flag
 1F3F3 200D 26A7 FE0F                                   ; unqualified         # ๐Ÿณโ€โšง๏ธ E13.0 transgender flag
-1F3F3 FE0F 200D 26A7                                   ; unqualified         # ๐Ÿณ๏ธโ€โšง E13.0 transgender flag
+1F3F3 FE0F 200D 26A7                                   ; minimally-qualified # ๐Ÿณ๏ธโ€โšง E13.0 transgender flag
 1F3F3 200D 26A7                                        ; unqualified         # ๐Ÿณโ€โšง E13.0 transgender flag
 1F3F4 200D 2620 FE0F                                   ; fully-qualified     # ๐Ÿดโ€โ˜ ๏ธ E11.0 pirate flag
 1F3F4 200D 2620                                        ; minimally-qualified # ๐Ÿดโ€โ˜  E11.0 pirate flag
@@ -4983,9 +5016,9 @@
 # Flags subtotal:		275	w/o modifiers
 
 # Status Counts
-# fully-qualified : 3624
-# minimally-qualified : 817
-# unqualified : 252
+# fully-qualified : 3655
+# minimally-qualified : 827
+# unqualified : 242
 # component : 9
 
 #EOF
diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex
index 00af77f57..a75d85c47 100644
--- a/lib/pleroma/object.ex
+++ b/lib/pleroma/object.ex
@@ -145,7 +145,7 @@ defmodule Pleroma.Object do
     Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
   end
 
-  def normalize(_, options \\ [fetch: false])
+  def normalize(_, options \\ [fetch: false, id_only: false])
 
   # If we pass an Activity to Object.normalize(), we can try to use the preloaded object.
   # Use this whenever possible, especially when walking graphs in an O(N) loop!
@@ -173,10 +173,15 @@ defmodule Pleroma.Object do
   def normalize(%{"id" => ap_id}, options), do: normalize(ap_id, options)
 
   def normalize(ap_id, options) when is_binary(ap_id) do
-    if Keyword.get(options, :fetch) do
-      Fetcher.fetch_object_from_id!(ap_id, options)
-    else
-      get_cached_by_ap_id(ap_id)
+    cond do
+      Keyword.get(options, :id_only) ->
+        ap_id
+
+      Keyword.get(options, :fetch) ->
+        Fetcher.fetch_object_from_id!(ap_id, options)
+
+      true ->
+        get_cached_by_ap_id(ap_id)
     end
   end
 
diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex
index a71a504b3..043a0643e 100644
--- a/lib/pleroma/signature.ex
+++ b/lib/pleroma/signature.ex
@@ -66,9 +66,8 @@ defmodule Pleroma.Signature do
     end
   end
 
-  def sign(%User{} = user, headers) do
-    with {:ok, %{keys: keys}} <- User.ensure_keys_present(user),
-         {:ok, private_key, _} <- Keys.keys_from_pem(keys) do
+  def sign(%User{keys: keys} = user, headers) do
+    with {:ok, private_key, _} <- Keys.keys_from_pem(keys) do
       HTTPSignatures.sign(private_key, user.ap_id <> "#main-key", headers)
     end
   end
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 4383f8f53..138f3c851 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -681,9 +681,9 @@ defmodule Pleroma.User do
     |> validate_exclusion(:nickname, Config.get([User, :restricted_nicknames]))
     |> validate_format(:nickname, local_nickname_regex())
     |> put_ap_id()
-    |> put_keys()
     |> unique_constraint(:ap_id)
     |> put_following_and_follower_and_featured_address()
+    |> put_private_key()
   end
 
   def register_changeset(struct, params \\ %{}, opts \\ []) do
@@ -741,10 +741,10 @@ defmodule Pleroma.User do
     |> validate_length(:registration_reason, max: reason_limit)
     |> maybe_validate_required_email(opts[:external])
     |> put_password_hash
-    |> put_keys()
     |> put_ap_id()
     |> unique_constraint(:ap_id)
     |> put_following_and_follower_and_featured_address()
+    |> put_private_key()
   end
 
   def maybe_validate_required_email(changeset, true), do: changeset
@@ -757,11 +757,6 @@ defmodule Pleroma.User do
     end
   end
 
-  def put_keys(changeset) do
-    {:ok, pem} = Keys.generate_rsa_pem()
-    put_change(changeset, :keys, pem)
-  end
-
   def put_ap_id(changeset) do
     ap_id = ap_id(%User{nickname: get_field(changeset, :nickname)})
     put_change(changeset, :ap_id, ap_id)
@@ -779,6 +774,11 @@ defmodule Pleroma.User do
     |> put_change(:featured_address, featured)
   end
 
+  defp put_private_key(changeset) do
+    {:ok, pem} = Keys.generate_rsa_pem()
+    put_change(changeset, :keys, pem)
+  end
+
   defp autofollow_users(user) do
     candidates = Config.get([:instance, :autofollowed_nicknames])
 
@@ -1955,6 +1955,7 @@ defmodule Pleroma.User do
       follower_address: uri <> "/followers"
     }
     |> change
+    |> put_private_key()
     |> unique_constraint(:nickname)
     |> Repo.insert()
     |> set_cache()
@@ -2220,17 +2221,6 @@ defmodule Pleroma.User do
     }
   end
 
-  def ensure_keys_present(%{keys: keys} = user) when not is_nil(keys), do: {:ok, user}
-
-  def ensure_keys_present(%User{} = user) do
-    with {:ok, pem} <- Keys.generate_rsa_pem() do
-      user
-      |> cast(%{keys: pem}, [:keys])
-      |> validate_required([:keys])
-      |> update_and_set_cache()
-    end
-  end
-
   def get_ap_ids_by_nicknames(nicknames) do
     from(u in User,
       where: u.nickname in ^nicknames,
diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
index 1eb0a3620..c07f91b2e 100644
--- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
@@ -66,8 +66,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
   end
 
   def user(conn, %{"nickname" => nickname}) do
-    with %User{local: true} = user <- User.get_cached_by_nickname(nickname),
-         {:ok, user} <- User.ensure_keys_present(user) do
+    with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
       conn
       |> put_resp_content_type("application/activity+json")
       |> put_view(UserView)
@@ -174,7 +173,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
 
   def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do
     with %User{} = user <- User.get_cached_by_nickname(nickname),
-         {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user),
          {:show_follows, true} <-
            {:show_follows, (for_user && for_user == user) || !user.hide_follows} do
       {page, _} = Integer.parse(page)
@@ -192,8 +190,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
   end
 
   def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname}) do
-    with %User{} = user <- User.get_cached_by_nickname(nickname),
-         {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do
+    with %User{} = user <- User.get_cached_by_nickname(nickname) do
       conn
       |> put_resp_content_type("application/activity+json")
       |> put_view(UserView)
@@ -213,7 +210,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
 
   def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do
     with %User{} = user <- User.get_cached_by_nickname(nickname),
-         {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user),
          {:show_followers, true} <-
            {:show_followers, (for_user && for_user == user) || !user.hide_followers} do
       {page, _} = Integer.parse(page)
@@ -231,8 +227,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
   end
 
   def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname}) do
-    with %User{} = user <- User.get_cached_by_nickname(nickname),
-         {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do
+    with %User{} = user <- User.get_cached_by_nickname(nickname) do
       conn
       |> put_resp_content_type("application/activity+json")
       |> put_view(UserView)
@@ -245,8 +240,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
         %{"nickname" => nickname, "page" => page?} = params
       )
       when page? in [true, "true"] do
-    with %User{} = user <- User.get_cached_by_nickname(nickname),
-         {:ok, user} <- User.ensure_keys_present(user) do
+    with %User{} = user <- User.get_cached_by_nickname(nickname) do
       # "include_poll_votes" is a hack because postgres generates inefficient
       # queries when filtering by 'Answer', poll votes will be hidden by the
       # visibility filter in this case anyway
@@ -270,8 +264,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
   end
 
   def outbox(conn, %{"nickname" => nickname}) do
-    with %User{} = user <- User.get_cached_by_nickname(nickname),
-         {:ok, user} <- User.ensure_keys_present(user) do
+    with %User{} = user <- User.get_cached_by_nickname(nickname) do
       conn
       |> put_resp_content_type("application/activity+json")
       |> put_view(UserView)
@@ -328,14 +321,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
   end
 
   defp represent_service_actor(%User{} = user, conn) do
-    with {:ok, user} <- User.ensure_keys_present(user) do
-      conn
-      |> put_resp_content_type("application/activity+json")
-      |> put_view(UserView)
-      |> render("user.json", %{user: user})
-    else
-      nil -> {:error, :not_found}
-    end
+    conn
+    |> put_resp_content_type("application/activity+json")
+    |> put_view(UserView)
+    |> render("user.json", %{user: user})
   end
 
   defp represent_service_actor(nil, _), do: {:error, :not_found}
@@ -388,12 +377,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
   def read_inbox(%{assigns: %{user: %User{nickname: nickname} = user}} = conn, %{
         "nickname" => nickname
       }) do
-    with {:ok, user} <- User.ensure_keys_present(user) do
-      conn
-      |> put_resp_content_type("application/activity+json")
-      |> put_view(UserView)
-      |> render("activity_collection.json", %{iri: "#{user.ap_id}/inbox"})
-    end
+    conn
+    |> put_resp_content_type("application/activity+json")
+    |> put_view(UserView)
+    |> render("activity_collection.json", %{iri: "#{user.ap_id}/inbox"})
   end
 
   def read_inbox(%{assigns: %{user: %User{nickname: as_nickname}}} = conn, %{
@@ -530,19 +517,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
     conn
   end
 
-  defp ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do
-    {:ok, new_user} = User.ensure_keys_present(user)
-
-    for_user =
-      if new_user != user and match?(%User{}, for_user) do
-        User.get_cached_by_nickname(for_user.nickname)
-      else
-        for_user
-      end
-
-    {new_user, for_user}
-  end
-
   def upload_media(%{assigns: %{user: %User{} = user}} = conn, %{"file" => file} = data) do
     with {:ok, object} <-
            ActivityPub.upload(
diff --git a/lib/pleroma/web/activity_pub/views/object_view.ex b/lib/pleroma/web/activity_pub/views/object_view.ex
index d9b59406c..29e2bbc81 100644
--- a/lib/pleroma/web/activity_pub/views/object_view.ex
+++ b/lib/pleroma/web/activity_pub/views/object_view.ex
@@ -29,11 +29,11 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do
 
   def render("object.json", %{object: %Activity{} = activity}) do
     base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header()
-    object = Object.normalize(activity, fetch: false)
+    object_id = Object.normalize(activity, id_only: true)
 
     additional =
       Transmogrifier.prepare_object(activity.data)
-      |> Map.put("object", object.data["id"])
+      |> Map.put("object", object_id)
 
     Map.merge(base, additional)
   end
diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex
index 760515f34..310f3ce3e 100644
--- a/lib/pleroma/web/activity_pub/views/user_view.ex
+++ b/lib/pleroma/web/activity_pub/views/user_view.ex
@@ -34,7 +34,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do
   def render("endpoints.json", _), do: %{}
 
   def render("service.json", %{user: user}) do
-    {:ok, user} = User.ensure_keys_present(user)
     {:ok, _, public_key} = Keys.keys_from_pem(user.keys)
     public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
     public_key = :public_key.pem_encode([public_key])
@@ -71,7 +70,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do
     do: render("service.json", %{user: user}) |> Map.put("preferredUsername", user.nickname)
 
   def render("user.json", %{user: user}) do
-    {:ok, user} = User.ensure_keys_present(user)
     {:ok, _, public_key} = Keys.keys_from_pem(user.keys)
     public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
     public_key = :public_key.pem_encode([public_key])
diff --git a/lib/pleroma/web/federator.ex b/lib/pleroma/web/federator.ex
index 82fb9e4e0..bc61130f1 100644
--- a/lib/pleroma/web/federator.ex
+++ b/lib/pleroma/web/federator.ex
@@ -69,10 +69,8 @@ defmodule Pleroma.Web.Federator do
   def perform(:publish, activity) do
     Logger.debug(fn -> "Running publish for #{activity.data["id"]}" end)
 
-    with %User{} = actor <- User.get_cached_by_ap_id(activity.data["actor"]),
-         {:ok, actor} <- User.ensure_keys_present(actor) do
-      Publisher.publish(actor, activity)
-    end
+    %User{} = actor = User.get_cached_by_ap_id(activity.data["actor"])
+    Publisher.publish(actor, activity)
   end
 
   def perform(:incoming_ap_doc, params) do
diff --git a/lib/pleroma/web/metadata/utils.ex b/lib/pleroma/web/metadata/utils.ex
index caca42934..8990bef54 100644
--- a/lib/pleroma/web/metadata/utils.ex
+++ b/lib/pleroma/web/metadata/utils.ex
@@ -8,8 +8,8 @@ defmodule Pleroma.Web.Metadata.Utils do
   alias Pleroma.Formatter
   alias Pleroma.HTML
 
-  def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do
-    content
+  defp scrub_html_and_truncate_object_field(field, object) do
+    field
     # html content comes from DB already encoded, decode first and scrub after
     |> HtmlEntities.decode()
     |> String.replace(~r/<br\s?\/?>/, " ")
@@ -19,6 +19,17 @@ defmodule Pleroma.Web.Metadata.Utils do
     |> Formatter.truncate()
   end
 
+  def scrub_html_and_truncate(%{data: %{"summary" => summary}} = object)
+      when is_binary(summary) and summary != "" do
+    summary
+    |> scrub_html_and_truncate_object_field(object)
+  end
+
+  def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do
+    content
+    |> scrub_html_and_truncate_object_field(object)
+  end
+
   def scrub_html_and_truncate(content, max_length \\ 200) when is_binary(content) do
     content
     |> scrub_html
diff --git a/lib/pleroma/web/plugs/o_auth_plug.ex b/lib/pleroma/web/plugs/o_auth_plug.ex
index 5e06ac3f6..91f6e9974 100644
--- a/lib/pleroma/web/plugs/o_auth_plug.ex
+++ b/lib/pleroma/web/plugs/o_auth_plug.ex
@@ -47,15 +47,17 @@ defmodule Pleroma.Web.Plugs.OAuthPlug do
   #
   @spec fetch_user_and_token(String.t()) :: {:ok, User.t(), Token.t()} | nil
   defp fetch_user_and_token(token) do
-    query =
+    token_query =
       from(t in Token,
-        where: t.token == ^token,
-        join: user in assoc(t, :user),
-        preload: [user: user]
+        where: t.token == ^token
       )
 
-    with %Token{user: user} = token_record <- Repo.one(query) do
+    with %Token{user_id: user_id} = token_record <- Repo.one(token_query),
+         false <- is_nil(user_id),
+         %User{} = user <- User.get_cached_by_id(user_id) do
       {:ok, user, token_record}
+    else
+      _ -> nil
     end
   end
 
diff --git a/lib/pleroma/web/web_finger.ex b/lib/pleroma/web/web_finger.ex
index b5f2cb72e..f5a46ce25 100644
--- a/lib/pleroma/web/web_finger.ex
+++ b/lib/pleroma/web/web_finger.ex
@@ -69,8 +69,6 @@ defmodule Pleroma.Web.WebFinger do
   end
 
   def represent_user(user, "JSON") do
-    {:ok, user} = User.ensure_keys_present(user)
-
     %{
       "subject" => "acct:#{user.nickname}@#{domain()}",
       "aliases" => gather_aliases(user),
@@ -79,8 +77,6 @@ defmodule Pleroma.Web.WebFinger do
   end
 
   def represent_user(user, "XML") do
-    {:ok, user} = User.ensure_keys_present(user)
-
     aliases =
       user
       |> gather_aliases()
diff --git a/priv/repo/migrations/20220905011454_generate_unset_user_keys.exs b/priv/repo/migrations/20220905011454_generate_unset_user_keys.exs
new file mode 100644
index 000000000..43bc7100b
--- /dev/null
+++ b/priv/repo/migrations/20220905011454_generate_unset_user_keys.exs
@@ -0,0 +1,28 @@
+# Pleroma: A lightweight social networking server
+# Copyright ยฉ 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Repo.Migrations.GenerateUnsetUserKeys do
+  use Ecto.Migration
+  import Ecto.Query
+  alias Pleroma.Keys
+  alias Pleroma.Repo
+  alias Pleroma.User
+
+  def change do
+    query =
+      from(u in User,
+        where: u.local == true,
+        where: is_nil(u.keys),
+        select: u
+      )
+
+    Repo.stream(query)
+    |> Enum.each(fn user ->
+      with {:ok, pem} <- Keys.generate_rsa_pem() do
+        Ecto.Changeset.cast(user, %{keys: pem}, [:keys])
+        |> Repo.update()
+      end
+    end)
+  end
+end
diff --git a/test/fixtures/rsa_keys/key_1.pem b/test/fixtures/rsa_keys/key_1.pem
new file mode 100644
index 000000000..3da357500
--- /dev/null
+++ b/test/fixtures/rsa_keys/key_1.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA2gdPJM5bWarGZ6QujfQ296l1yEQohS5fdtnxYQc+RXuS1gqZ
+R/jVGHG25o4tmwyCLClyREU1CBTOCQBsg+BSehXlxNR9fiB4KaVQW9MMNa2vhHuG
+f7HLdILiC+SPPTV1Bi8LCpxJowiSpnFPP4BDDeRKib7nOxll9Ln9gEpUueKKabsQ
+EQKCmEJYhIz/8g5R0Qz+6VjASdejDjTEdZbr/rwyldRRjIklyeZ3lBzB/c8/51wn
+HT2Dt0r9NiapxYC3oNhbE2A+4FU9pZTqS8yc3KqWZAy74snaRO9QQSednKlOJpXP
+V3vwWo5CxuSNLttV7zRcrqeYOkIVNF4dQ/bHzQIDAQABAoIBADTCfglnEj4BkF92
+IHnjdgW6cTEUJUYNMba+CKY1LYF85Mx85hi/gzmWEu95yllxznJHWUpiAPJCrpUJ
+EDldaDf44pAd53xE+S8CvQ5rZNH8hLOnfKWb7aL1JSRBm9PxAq+LZL2dkkgsg+hZ
+FRdFv3Q2IT9x/dyUSdLNyyVnV1dfoya/7zOFc7+TwqlofznzrlBgNoAe8Lb4AN/q
+itormPxskqATiq11XtP4F6eQ556eRgHCBxmktx/rRDl6f9G9dvjRQOA2qZlHQdFq
+kjOZsrvItL46LdVoLPOdCYG+3HFeKoDUR1NNXEkt66eqmEhLY4MgzGUT1wqXWk7N
+XowZc9UCgYEA+L5h4PhANiY5Kd+PkRI8zTlJMv8hFqLK17Q0p9eL+mAyOgXjH9so
+QutJf4wU+h6ESDxH+1tCjCN307uUqT7YnT2zHf3b6GcmA+t6ewxfxOY2nJ82HENq
+hK1aodnPTvRRRqCGfrx9qUHRTarTzi+2u86zH+KoMHSiuzn4VpQhg4MCgYEA4GOL
+1tLR9+hyfYuMFo2CtQjp3KpJeGNKEqc33vFD05xJQX+m5THamBv8vzdVlVrMh/7j
+iV85mlA7HaaP+r5DGwtonw9bqY76lYRgJJprsS5lHcRnXsDmU4Ne8RdB3dHNsT5P
+n4P6v8y4jaT638iJ/qLt4e8itOBlZwS//VIglm8CgYEA7KXD3RKRlHK9A7drkOs2
+6VBM8bWEN1LdhGYvilcpFyUZ49XiBVatcS0EGdKdym/qDgc7vElQgJ7ly4y0nGfs
+EXy3whrYcrxfkG8hcZuOKXeUEWHvSuhgmKWMilr8PfN2t6jVDBIrwzGY/Tk+lPUT
+9o1qITW0KZVtlI5MU6JOWB0CgYAHwwnETZibxbuoIhqfcRezYXKNgop2EqEuUgB5
+wsjA2igijuLcDMRt/JHan3RjbTekAKooR1X7w4i39toGJ2y008kzr1lRXTPH1kNp
+ILpW767pv7B/s5aEDwhKuK47mRVPa0Nf1jXnSpKbu7g943b6ivJFnXsK3LRFQwHN
+JnkgGwKBgGUleQVd2GPr1dkqLVOF/s2aNB/+h2b1WFWwq0YTnW81OLwAcUVE4p58
+3GQgz8PCsWbNdTb9yFY5fq0fXgi0+T54FEoZWH09DrOepA433llAwI6sq7egrFdr
+kKQttZMzs6ST9q/IOF4wgqSnBjjTC06vKSkNAlXJz+LMvIRMeBr0
+-----END RSA PRIVATE KEY-----
diff --git a/test/fixtures/rsa_keys/key_2.pem b/test/fixtures/rsa_keys/key_2.pem
new file mode 100644
index 000000000..7a8e8e670
--- /dev/null
+++ b/test/fixtures/rsa_keys/key_2.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAwu0VqVGRVDW09V3zZ0+08K9HMKivIzIInO0xim3jbfVcg8r1
+sR7vNLorYAB6TDDlXYAWKx1OxUMZusbOigrpQd+5wy8VdCogDD7qk4bbZ+NjXkuD
+ETzrQsGWUXe+IdeH8L0Zh0bGjbarCuA0qAeY1TEteGl+Qwo2dsrBUH7yKmWO6Mz9
+XfPshrIDOGo4QNyVfEBNGq2K9eRrQUHeAPcM2/qu4ZAZRK+VCifDZrF8ZNpoAsnS
+R2mJDhOBUMvI/ZaxOc2ry4EzwcS4uBaM2wONkGWDaqO6jNAQflaX7vtzOAeJB7Dt
+VKXUUcZAGN7uI3c2mG5IKGMhTYUtUdrzmqmtZwIDAQABAoIBAQCHBJfTf3dt4AGn
+T9twfSp06MQj9UPS2i5THI0LONCm8qSReX0zoZzJZgbzaYFM0zWczUMNvDA6vR7O
+XDTmM2acxW4zv6JZo3Ata0sqwuepDz1eLGnt/8dppxQK/ClL4bH8088h/6k6sgPJ
+9cEjfpejXHwFgvT9VM6i/BBpRHVTXWuJqwpDtg+bleQNN3L3RapluDd7BGiKoCwQ
+cCTKd+lxTu9gVJkbRTI/Jn3kV+rnedYxHTxVp5cU1qIabsJWBcdDz25mRHupxQsn
+JbQR4+ZnRLeAsC6WJZtEJz2KjXgBaYroHbGZY3KcGW95ILqiCJoJJugbW1eABKnN
+Q5k8XVspAoGBAPzGJBZuX3c0quorhMIpREmGq2vS6VCQwLhH5qayYYH1LiPDfpdq
+69lOROxZodzLxBgTf5z/a5kBF+eNKvOqfZJeRTxmllxxO1MuJQuRLi/b7BHHLuyN
+Eea+YwtehA0T0CbD2hydefARNDruor2BLvt/kt6qEoIFiPauTsMfXP39AoGBAMVp
+8argtnB+vsk5Z7rpQ4b9gF5QxfNbA0Hpg5wUUdYrUjFr50KWt1iowj6AOVp/EYgr
+xRfvOQdYODDH7R5cjgMbwvtpHo39Zwq7ewaiT1sJXnpGmCDVh+pdTHePC5OOXnxN
+0USK3M4KjltjVqJo7xPPElgJvCejudD47mtHMaQzAoGBAIFQ/PVc0goyL55NVUXf
+xse21cv7wtEsvOuKHT361FegD1LMmN7uHGq32BryYBSNSmzmzMqNAYbtQEV9uxOd
+jVBsWg9kjFgOtcMAQIOCapahdExEEoWCRj49+H3AhN4L3Nl4KQWqqs9efdIIc8lv
+ZZHU2lZ/u6g5HLDWzASW7wQhAoGAdERPRrqN+HdNWinrA9Q6JxjKL8IWs5rYsksb
+biMxh5eAEwdf7oHhfd/2duUB4mCQLMjKjawgxEia33AAIS+VnBMPpQ5mJm4l79Y3
+QNL7Nbyw3gcRtdTM9aT5Ujj3MnJZB5C1PU8jeF4TNZOuBH0UwW/ld+BT5myxFXhm
+wtvtSq0CgYEA19b0/7il4Em6uiLOmYUuqaUoFhUPqzjaS6OM/lRAw12coWv/8/1P
+cwaNZHNMW9Me/bNH3zcOTz0lxnYp2BeRehjFYVPRuS1GU7uwqKtlL2wCPptTfAhN
+aJWIplzUCTg786u+sdNZ0umWRuCLoUpsKTgP/yt4RglzEcfxAuBDljk=
+-----END RSA PRIVATE KEY-----
diff --git a/test/fixtures/rsa_keys/key_3.pem b/test/fixtures/rsa_keys/key_3.pem
new file mode 100644
index 000000000..fbd25c80f
--- /dev/null
+++ b/test/fixtures/rsa_keys/key_3.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEA0GvzqZ3r78GLa7guGn+palKRLGru4D4jnriHgfrUAJrdLyZ5
+9d0zAA4qnS2L6YAMoPPBhBUtIV5e2sn1+rwTClWU3dm3FyBAeqdeIBKN+04AyrUc
+HXYaZtOPJXCTeytzoSQE359Tq6+xwgoHlUWSWxQF51/z/PDQcUvqFjJqAtdiDchd
+3CiFRtdjegyxXGnqvPmBix+vEjDytcVydfch+R1Twf6f5EL7a1jFVWNGcratYBEl
+nqOWKI2fBu/WA8QlrcVW5zmtZo9aJ6IrFddQgQTxPk/mEHgCzv8tbCRI9TxiXeYH
+YqxZFYBW40xbZQwGRjaYHJlIRYp9+TOynW9OZQIDAQABAoIBAQC97cIMDbdVsyAk
+N6D70N5H35ofygqJGtdG6o3B6xuKuZVaREvbu4mgQUigF0Nqs5/OhJMSlGGeCOuT
+oXug1Abd4gNY7++jCWb43tAtlfsAyaJ7FvPZ/SguEBhgW+hp07z5WWN/jSeoSuFI
+G++xHcczbFm88XncRG8O78kQFTz5/DlQYkFXfbqpuS3BqxnrACpDCUfrUwZNYFIp
+CUNq21jdifhHwlS0K3PX8A5HdOYeVnVHaE78LGE4oJVHwcokELv+PYqarWZq/a6L
+vKU3yn2+4pj2WO490iGQaRKVM35vrtjdVxiWEIUiFc3Jg5fKZA3wuHXoF1N1DpPO
+BO6Att55AoGBAP/nC2szmDcnU5Sh8LDeQbL+FpSBwOmFnmel5uqbjKnDzf9emPQu
+NFUls1N9OGgyUq08TnmcY/7wLZzcu7Y9XOUURuYtx9nGRs4RmE2VEBhK1r7CkDIx
+oOb+NtdqnPtQASAxCHszoGCFxpuV7UVoo2SRgc+M4ceX128arvBUtvdrAoGBANCA
+RuO3eelkXaJoCeogEUVWXZ6QmPeYzbMD4vg2DM0ynUbReyuEIIhn+SR7tehlj5ie
+4T3ixVdur6k+YUdiFhUYgXaHBJWHoHl1lrU3ZON8n7AeEk9ft6gg4L07ouj78UMZ
+sArJIlU5mLnW02zbV9XryU39dIgpQREqC0bIOtVvAoGBAORv1JKq6Rt7ALJy6VCJ
+5y4ogfGp7pLHk8NEpuERYDz/rLllMbbwNAk6cV17L8pb+c/pQMhwohcnQiCALxUc
+q/tW4X+CqJ+vzu8PZ90Bzu9Qh2iceGpGQTNTBZPA+UeigI7DFqYcTPM9GDE1YiyO
+nyUcezvSsI4i7s6gjD+/7+DnAoGABm3+QaV1z/m1XX3B2IN2pOG971bcML54kW2s
+QSVBjc5ixT1OhBAGBM7YAwUBnhILtJQptAPbPBAAwMJYs5/VuH7R9zrArG/LRhOX
+Oy1jIhTEw+SZgfMcscWZyJwfMPob/Yq8QAjl0yT8jbaPPIsjEUi9I3eOcWh8RjA6
+ussP7WcCgYEAm3yvJR9z6QGoQQwtDbwjyZPYOSgK9wFS/65aupi6cm/Qk2N1YaLY
+q2amNrzNsIc9vQwYGEHUwogn4MieHk96V7m2f0Hx9EHCMwizU9EiS6oyiLVowTG6
+YsBgSzcpnt0Vkgil4CQks5uQoan0tubEUQ5DI79lLnb02n4o46iAYK0=
+-----END RSA PRIVATE KEY-----
diff --git a/test/fixtures/rsa_keys/key_4.pem b/test/fixtures/rsa_keys/key_4.pem
new file mode 100644
index 000000000..f72b29fb1
--- /dev/null
+++ b/test/fixtures/rsa_keys/key_4.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAw6MLRbP/henX2JxwdMkQlskKghBoMyUPu9kZpUQ9yYfIm9I4
+a3gEfzef75jKLOSf+BkZulvEUGjC+VnkpV3s+OZCSq81Ykv5PHuTqbj8Cn/dEt/g
+lBXxPcOBKWqa+1cDX6QVIVJsBihLB/1b64H3U96Yu9+knmXvT1Az5MFA2KtSq7HJ
+O+GJNn0EMI7xwPz/atUGlMLrhzwS4UDpw9CAaRPojplJYl4K1JMCFTgTt3hJILXZ
+tw1MKTeeyWzNiuQRBQJuCnqfvsBYsasIlHWfqIL/uBzcGHHCIK5ZW9luntJXyLVj
+zzaF7etIJk1uddM2wnqOOaVyqbssZXGt7Tb9IQIDAQABAoIBAH5QJRUKFK8Xvp9C
+0nD06NsSTtCPW1e6VCBLGf3Uw7f9DY9d+cOZp/2jooYGNnMp4gdD3ZKvcV8hZNGu
+Mqx6qmhB8wdZfLRMrU1Z1Is+vqzgxZJMLiouyKXCNwDQreQd2DXGMUZkew62sUsl
+UFYMge4KyL50tUr4Mb0Z4YePJxk804tcqgw0n+D0lR7ZKhSqoQpoMqEiO+27Yw7E
+Txj/MKH8f/ZJ6LBLRISOdBOrxonHqqeYWchczykCwojOZc3bIlWZGhg727dFTHDC
+yrj3/zsZ2hy+TQsucCFY0RljIbacmHvrF/VqfhTIhg98H0F27V/jiPGsdKhptyst
+E9iQVMkCgYEA42ge4H2Wl42sRh61GOrOgzzr0WZS54bF5skMxiGGnLwnb82rwUBt
+xw94PRORJbV9l+2fkxbfiW0uzornfN8OBHSB64Pcjzzbl5Qm+eaDOiuTLtakYOWQ
+/ipGqw8iE4J9iRteZCo8GnMxWbTkYCporTlFDTeYguXmwR4yCXtlCbMCgYEA3DxM
+7R5HMUWRe64ucdekMh742McS8q/X5jdN9iFGy0M8P1WTyspSlaPDXgjaO4XqpRqg
+djkL993kCDvOAiDl6Tpdiu1iFcOaRLb19Tj1pm8sKdk6X4d10U9lFri4NVYCmvVi
+yOahUYFK/k5bA+1o+KU9Pi82H36H3WNeF4evC9sCgYEAs1zNdc04uQKiTZAs0KFr
+DzI+4aOuYjT35ObQr3mD/h2dkV6MSNmzfF1kPfAv/KkgjXN7+H0DBRbb40bF/MTF
+/peSXZtcnJGote7Bqzu4Z2o1Ja1ga5jF+uKHaKZ//xleQIUYtzJkw4v18cZulrb8
+ZxyTrTAbl6sTjWBuoPH1qGcCgYEAsQNahR9X81dKJpGKTQAYvhw8wOfI5/zD2ArN
+g62dXBRPYUxkPJM/q3xzs6oD1eG+BjQPktYpM3FKLf/7haRxhnLd6qL/uiR8Ywx3
+RkEg2EP0yDIMA+o5nSFmS8vuaxgVgf0HCBiuwnbcEuhhqRdxzp/pSIjjxI6LnzqV
+zu3EmQ8CgYEAhq8Uhvw+79tK7q2PCjDbiucA0n/4a3aguuvRoEh7F93Pf6VGZmT+
+Yld54Cd4P5ATI3r5YdD+JBuvgNMOTVPCaD/WpjbJKnrpNEXtXRQD6LzAXZDNk0sF
+IO9i4gjhBolRykWn10khoPdxw/34FWBP5SxU1JYk75NQXvI3TD+5xbU=
+-----END RSA PRIVATE KEY-----
diff --git a/test/fixtures/rsa_keys/key_5.pem b/test/fixtures/rsa_keys/key_5.pem
new file mode 100644
index 000000000..49342b54e
--- /dev/null
+++ b/test/fixtures/rsa_keys/key_5.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpgIBAAKCAQEA0jdKtMkgqnEGO3dn4OKxtggfFDzv+ddXToO0cdPXkUgPajCo
+UGPunz+A1KmkAmLY0Vwk0tkOmKK8GFHek/5zQ+1N2FHBi19fbwlJk7hzh5OiYRhu
+YZi0d6LsqEMKhDk6NqIeiFmOe2YHgklVvZV0hebvHlHLgzDhYrDltSPe33UZa3MS
+g2Knf4WQAjLOo2BAb+oyj/UNXeAqaMGcOr6/kAHPcODW2EGhF3H3umFLv7t/Kq5i
+WPBgarbCGPR5qq9SW5ZIjS3Sz0dl105Grw8wU23CC/2IBZ5vNiu+bkmLEoh/KpX2
+YBILoLmwtVX0Qxc15CrpOi12p+/4pLR8kuEowQIDAQABAoIBAQDMDQ3AJMdHisSQ
+7pvvyDzWRFXesDQE4YmG1gNOxmImTLthyW9n8UjMXbjxNOXVxxtNRdMcs8MeWECa
+nsWeBEzgr7VzeBCV9/LL9kjsUgwamyzwcOWcaL0ssAJmZgUMSfx+0akvkzbiAyzg
+w8ytZSihXYPYe28/ni/5O1sOFI6feenOnJ9NSmVUA24c9TTJGNQs7XRUMZ8f9wt6
+KwRmYeNDKyqH7NvLmmKoDp6m7bMDQxWArVTAoRWTVApnj35iLQtmSi8DBdw6xSzQ
+fKpUe/B4iQmMNxUW7KmolOvCIS5wcYZJE+/j7xshA2GGnOpx4aC+N+w2GSX4Bz/q
+OnYSpGUBAoGBAOwnSeg17xlZqmd86qdiCxg0hRtAjwrd7btYq6nkK+t9woXgcV99
+FBS3nLbk/SIdXCW8vHFJTmld60j2q2kdestYBdHznwNZJ4Ee8JhamzcC64wY7O0x
+RameO/6uoKS4C3VF+Zc9CCPfZOqYujkGvSqbTjFZWuFtDp0GHDk+qEIRAoGBAOPh
++PCB2QkGgiujSPmuCT5PTuNylAug3D4ZdMRKpQb9Rnzlia1Rpdrihq+PvB2vwa+S
+mB6dgb0E7M2AyEMVu5buris0mVpRdmEeLCXR8mYJ48kOslIGArEStXDetfbRaXdK
+7vf4APq2d78AQYldU2fYlo754Dh/3MZIguzpqMuxAoGBAIDJqG/AQiYkFV+c62ff
+e0d3FQRYv+ngQE9Eu1HKwv0Jt7VFQu8din8F56yC013wfxmBhY+Ot/mUo8VF6RNJ
+ZXdSCNKINzcfPwEW+4VLHIzyxbzAty1gCqrHRdbOK4PJb05EnCqTuUW/Bg0+v4hs
+GWwMCKe3IG4CCM8vzuKVPjPRAoGBANYCQtJDb3q9ZQPsTb1FxyKAQprx4Lzm7c9Y
+AsPRQhhFRaxHuLtPQU5FjK1VdBoBFAl5x2iBDPVhqa348pml0E0Xi/PBav9aH61n
+M5i1CUrwoL4SEj9bq61133XHgeXwlnZUpgW0H99T+zMh32pMfea5jfNqETueQMzq
+DiLF8SKRAoGBAOFlU0kRZmAx3Y4rhygp1ydPBt5+zfDaGINRWEN7QWjhX2QQan3C
+SnXZlP3POXLessKxdCpBDq/RqVQhLea6KJMfP3F0YbohfWHt96WjiriJ0d0ZYVhu
+34aUM2UGGG0Kia9OVvftESBaXk02vrY9zU3LAVAv0eLgIADm1kpj85v7
+-----END RSA PRIVATE KEY-----
diff --git a/test/pleroma/user_test.exs b/test/pleroma/user_test.exs
index 645622e43..9d35f9780 100644
--- a/test/pleroma/user_test.exs
+++ b/test/pleroma/user_test.exs
@@ -620,15 +620,15 @@ defmodule Pleroma.UserTest do
       assert changeset.valid?
     end
 
-    test "it sets the password_hash, ap_id and PEM key" do
+    test "it sets the password_hash, ap_id, private key and followers collection address" do
       changeset = User.register_changeset(%User{}, @full_user_data)
 
       assert changeset.valid?
 
       assert is_binary(changeset.changes[:password_hash])
+      assert is_binary(changeset.changes[:keys])
       assert changeset.changes[:ap_id] == User.ap_id(%User{nickname: @full_user_data.nickname})
       assert is_binary(changeset.changes[:keys])
-
       assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
     end
 
@@ -2130,21 +2130,6 @@ defmodule Pleroma.UserTest do
     end
   end
 
-  describe "ensure_keys_present" do
-    test "it creates keys for a user and stores them in info" do
-      user = insert(:user)
-      refute is_binary(user.keys)
-      {:ok, user} = User.ensure_keys_present(user)
-      assert is_binary(user.keys)
-    end
-
-    test "it doesn't create keys if there already are some" do
-      user = insert(:user, keys: "xxx")
-      {:ok, user} = User.ensure_keys_present(user)
-      assert user.keys == "xxx"
-    end
-  end
-
   describe "get_ap_ids_by_nicknames" do
     test "it returns a list of AP ids for a given set of nicknames" do
       user = insert(:user)
diff --git a/test/pleroma/web/activity_pub/views/object_view_test.exs b/test/pleroma/web/activity_pub/views/object_view_test.exs
index 923515dec..9348c09be 100644
--- a/test/pleroma/web/activity_pub/views/object_view_test.exs
+++ b/test/pleroma/web/activity_pub/views/object_view_test.exs
@@ -81,4 +81,18 @@ defmodule Pleroma.Web.ActivityPub.ObjectViewTest do
     assert result["object"] == object.data["id"]
     assert result["type"] == "Announce"
   end
+
+  test "renders an undo announce activity" do
+    note = insert(:note_activity)
+    user = insert(:user)
+
+    {:ok, announce} = CommonAPI.repeat(note.id, user)
+    {:ok, undo} = CommonAPI.unrepeat(note.id, user)
+
+    result = ObjectView.render("object.json", %{object: undo})
+
+    assert result["id"] == undo.data["id"]
+    assert result["object"] == announce.data["id"]
+    assert result["type"] == "Undo"
+  end
 end
diff --git a/test/pleroma/web/activity_pub/views/user_view_test.exs b/test/pleroma/web/activity_pub/views/user_view_test.exs
index e49cb99d3..5501e64d6 100644
--- a/test/pleroma/web/activity_pub/views/user_view_test.exs
+++ b/test/pleroma/web/activity_pub/views/user_view_test.exs
@@ -12,7 +12,6 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
 
   test "Renders a user, including the public key" do
     user = insert(:user)
-    {:ok, user} = User.ensure_keys_present(user)
 
     result = UserView.render("user.json", %{user: user})
 
@@ -55,7 +54,6 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
 
   test "Does not add an avatar image if the user hasn't set one" do
     user = insert(:user)
-    {:ok, user} = User.ensure_keys_present(user)
 
     result = UserView.render("user.json", %{user: user})
     refute result["icon"]
@@ -67,8 +65,6 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
         banner: %{"url" => [%{"href" => "https://somebanner"}]}
       )
 
-    {:ok, user} = User.ensure_keys_present(user)
-
     result = UserView.render("user.json", %{user: user})
     assert result["icon"]["url"] == "https://someurl"
     assert result["image"]["url"] == "https://somebanner"
@@ -89,7 +85,6 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
   describe "endpoints" do
     test "local users have a usable endpoints structure" do
       user = insert(:user)
-      {:ok, user} = User.ensure_keys_present(user)
 
       result = UserView.render("user.json", %{user: user})
 
@@ -105,7 +100,6 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
 
     test "remote users have an empty endpoints structure" do
       user = insert(:user, local: false)
-      {:ok, user} = User.ensure_keys_present(user)
 
       result = UserView.render("user.json", %{user: user})
 
@@ -115,7 +109,6 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
 
     test "instance users do not expose oAuth endpoints" do
       user = insert(:user, nickname: nil, local: true)
-      {:ok, user} = User.ensure_keys_present(user)
 
       result = UserView.render("user.json", %{user: user})
 
diff --git a/test/pleroma/web/metadata/providers/twitter_card_test.exs b/test/pleroma/web/metadata/providers/twitter_card_test.exs
index 1b8d27cda..5d7ad08ef 100644
--- a/test/pleroma/web/metadata/providers/twitter_card_test.exs
+++ b/test/pleroma/web/metadata/providers/twitter_card_test.exs
@@ -39,6 +39,7 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do
           "actor" => user.ap_id,
           "tag" => [],
           "id" => "https://pleroma.gov/objects/whatever",
+          "summary" => "",
           "content" => "pleroma in a nutshell"
         }
       })
@@ -54,6 +55,36 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do
            ] == result
   end
 
+  test "it uses summary as description if post has one" do
+    user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
+    {:ok, activity} = CommonAPI.post(user, %{status: "HI"})
+
+    note =
+      insert(:note, %{
+        data: %{
+          "actor" => user.ap_id,
+          "tag" => [],
+          "id" => "https://pleroma.gov/objects/whatever",
+          "summary" => "Public service announcement on caffeine consumption",
+          "content" => "cofe"
+        }
+      })
+
+    result = TwitterCard.build_tags(%{object: note, user: user, activity_id: activity.id})
+
+    assert [
+             {:meta, [property: "twitter:title", content: Utils.user_name_string(user)], []},
+             {:meta,
+              [
+                property: "twitter:description",
+                content: "Public service announcement on caffeine consumption"
+              ], []},
+             {:meta, [property: "twitter:image", content: "http://localhost:4001/images/avi.png"],
+              []},
+             {:meta, [property: "twitter:card", content: "summary"], []}
+           ] == result
+  end
+
   test "it renders avatar not attachment if post is nsfw and unfurl_nsfw is disabled" do
     clear_config([Pleroma.Web.Metadata, :unfurl_nsfw], false)
     user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
@@ -65,6 +96,7 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do
           "actor" => user.ap_id,
           "tag" => [],
           "id" => "https://pleroma.gov/objects/whatever",
+          "summary" => "",
           "content" => "pleroma in a nutshell",
           "sensitive" => true,
           "attachment" => [
@@ -109,6 +141,7 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do
           "actor" => user.ap_id,
           "tag" => [],
           "id" => "https://pleroma.gov/objects/whatever",
+          "summary" => "",
           "content" => "pleroma in a nutshell",
           "attachment" => [
             %{
diff --git a/test/pleroma/web/metadata/utils_test.exs b/test/pleroma/web/metadata/utils_test.exs
index c99d11596..665efb9ca 100644
--- a/test/pleroma/web/metadata/utils_test.exs
+++ b/test/pleroma/web/metadata/utils_test.exs
@@ -8,7 +8,7 @@ defmodule Pleroma.Web.Metadata.UtilsTest do
   alias Pleroma.Web.Metadata.Utils
 
   describe "scrub_html_and_truncate/1" do
-    test "it returns text without encode HTML" do
+    test "it returns content text without encode HTML if summary is nil" do
       user = insert(:user)
 
       note =
@@ -16,6 +16,7 @@ defmodule Pleroma.Web.Metadata.UtilsTest do
           data: %{
             "actor" => user.ap_id,
             "id" => "https://pleroma.gov/objects/whatever",
+            "summary" => nil,
             "content" => "Pleroma's really cool!"
           }
         })
@@ -23,6 +24,39 @@ defmodule Pleroma.Web.Metadata.UtilsTest do
       assert Utils.scrub_html_and_truncate(note) == "Pleroma's really cool!"
     end
 
+    test "it returns context text without encode HTML if summary is empty" do
+      user = insert(:user)
+
+      note =
+        insert(:note, %{
+          data: %{
+            "actor" => user.ap_id,
+            "id" => "https://pleroma.gov/objects/whatever",
+            "summary" => "",
+            "content" => "Pleroma's really cool!"
+          }
+        })
+
+      assert Utils.scrub_html_and_truncate(note) == "Pleroma's really cool!"
+    end
+
+    test "it returns summary text without encode HTML if summary is filled" do
+      user = insert(:user)
+
+      note =
+        insert(:note, %{
+          data: %{
+            "actor" => user.ap_id,
+            "id" => "https://pleroma.gov/objects/whatever",
+            "summary" => "Public service announcement on caffeine consumption",
+            "content" => "cofe"
+          }
+        })
+
+      assert Utils.scrub_html_and_truncate(note) ==
+               "Public service announcement on caffeine consumption"
+    end
+
     test "it does not return old content after editing" do
       user = insert(:user)
 
diff --git a/test/support/factory.ex b/test/support/factory.ex
index 6695886dc..efcd8039e 100644
--- a/test/support/factory.ex
+++ b/test/support/factory.ex
@@ -10,6 +10,15 @@ defmodule Pleroma.Factory do
   alias Pleroma.Object
   alias Pleroma.User
 
+  @rsa_keys [
+              "test/fixtures/rsa_keys/key_1.pem",
+              "test/fixtures/rsa_keys/key_2.pem",
+              "test/fixtures/rsa_keys/key_3.pem",
+              "test/fixtures/rsa_keys/key_4.pem",
+              "test/fixtures/rsa_keys/key_5.pem"
+            ]
+            |> Enum.map(&File.read!/1)
+
   def participation_factory do
     conversation = insert(:conversation)
     user = insert(:user)
@@ -28,6 +37,8 @@ defmodule Pleroma.Factory do
   end
 
   def user_factory(attrs \\ %{}) do
+    pem = Enum.random(@rsa_keys)
+
     user = %User{
       name: sequence(:name, &"Test ใƒ†ใ‚นใƒˆ User #{&1}"),
       email: sequence(:email, &"user#{&1}@example.com"),
@@ -39,7 +50,8 @@ defmodule Pleroma.Factory do
       last_refreshed_at: NaiveDateTime.utc_now(),
       notification_settings: %Pleroma.User.NotificationSetting{},
       multi_factor_authentication_settings: %Pleroma.MFA.Settings{},
-      ap_enabled: true
+      ap_enabled: true,
+      keys: pem
     }
 
     urls =