diff --git a/docs/docs/installation/openbsd_en.md b/docs/docs/installation/openbsd_en.md
index 7b20c796a..51909fdd7 100644
--- a/docs/docs/installation/openbsd_en.md
+++ b/docs/docs/installation/openbsd_en.md
@@ -1,6 +1,6 @@
 # Installing on OpenBSD
 
-This guide describes the installation and configuration of akkoma (and the required software to run it) on a single OpenBSD 6.6 server.
+This guide describes the installation and configuration of akkoma (and the required software to run it) on a single OpenBSD 7.2 server.
 
 For any additional information regarding commands and configuration files mentioned here, check the man pages [online](https://man.openbsd.org/) or directly on your server with the man command.
 
@@ -12,11 +12,10 @@ For any additional information regarding commands and configuration files mentio
 To install them, run the following command (with doas or as root):
 
 ```
-pkg_add elixir gmake git postgresql-server postgresql-contrib cmake ffmpeg ImageMagick erlang-wx-25
+pkg_add elixir gmake git postgresql-server postgresql-contrib cmake ffmpeg erlang-wx libmagic
+pkg_add erlang-wx # Choose the latest version as package version when promted
 ```
 
-(Note that the erlang version may change, it was 25 at the time of writing)
-
 Akkoma requires a reverse proxy, OpenBSD has relayd in base (and is used in this guide) and packages/ports are available for nginx (www/nginx) and apache (www/apache-httpd). Independently of the reverse proxy, [acme-client(1)](https://man.openbsd.org/acme-client) can be used to get a certificate from Let's Encrypt.
 
 #### Optional software
@@ -29,32 +28,35 @@ Per [`docs/installation/optional/media_graphics_packages.md`](../installation/op
 To install the above:
 
 ```
-pkg_add ImageMagick ffmpeg p5-Image-ExifTool
+pkg_add ffmpeg p5-Image-ExifTool
 ```
 
 #### Creating the akkoma user
-Akkoma will be run by a dedicated user, \_akkoma. Before creating it, insert the following lines in login.conf:
+Akkoma will be run by a dedicated user, `_akkoma`. Before creating it, insert the following lines in `/etc/login.conf`:
 ```
 akkoma:\
 	:datasize-max=1536M:\
 	:datasize-cur=1536M:\
 	:openfiles-max=4096
 ```
-This creates a "akkoma" login class and sets higher values than default for datasize and openfiles (see [login.conf(5)](https://man.openbsd.org/login.conf)), this is required to avoid having akkoma crash some time after starting.
+This creates a `akkoma` login class and sets higher values than default for datasize and openfiles (see [login.conf(5)](https://man.openbsd.org/login.conf)), this is required to avoid having akkoma crash some time after starting.
 
-Create the \_akkoma user, assign it the akkoma login class and create its home directory (/home/\_akkoma/): `useradd -m -L akkoma _akkoma`
+Create the `_akkoma` user, assign it the akkoma login class and create its home directory (`/home/_akkoma/`): `useradd -m -L akkoma _akkoma`
 
 #### Clone akkoma's directory
-Enter a shell as the \_akkoma user. As root, run `su _akkoma -;cd`. Then clone the repository with `git clone https://akkoma.dev/AkkomaGang/akkoma.git`. Akkoma is now installed in /home/\_akkoma/akkoma/, it will be configured and started at the end of this guide.
+Enter a shell as the `_akkoma` user. As root, run `su _akkoma -;cd`. Then clone the repository with `git clone https://akkoma.dev/AkkomaGang/akkoma.git`. Akkoma is now installed in `/home/_akkoma/akkoma/`, it will be configured and started at the end of this guide.
 
 #### PostgreSQL
-Start a shell as the \_postgresql user (as root run `su _postgresql -` then run the `initdb` command to initialize postgresql:
-You will need to specify pgdata directory to the default (/var/postgresql/data) with the `-D <path>` and set the user to postgres with the `-U <username>` flag. This can be done as follows:
+Create `_postgresql`'s user directory (it hasn't been created yet): `mdir var/postgresql/data`. To set it as home
+directory for user `_postgresql` run `usermod -d  /var/postgresql/data _postgresql`.
+
+Start a shell as the `_postgresql` user (as root run `su _postgresql -` then run the `initdb` command to initialize postgresql.
+You will need to specify pgdata directory to the default (`/var/postgresql/data`) with the `-D <path>` and set the user to postgres with the `-U <username>` flag. This can be done as follows:
 
 ```
 initdb -D /var/postgresql/data -U postgres
 ```
-If you are not using the default directory, you will have to update the `datadir` variable in the /etc/rc.d/postgresql script.
+If you are not using the default directory, you will have to update the `datadir` variable in the `/etc/rc.d/postgresql` script.
 
 When this is done, enable postgresql so that it starts on boot and start it. As root, run:
 ```
@@ -70,7 +72,7 @@ httpd will have three fuctions:
   * serve a robots.txt file
   * get Let's Encrypt certificates, with acme-client
 
-Insert the following config in httpd.conf:
+Insert the following config in `/etc/httpd.conf`:
 ```
 # $OpenBSD: httpd.conf,v 1.17 2017/04/16 08:50:49 ajacoutot Exp $
 
@@ -93,13 +95,10 @@ server "default" {
 	location "/robots.txt" { root "/htdocs/local/" }
 	location "/*" { block return 302 "https://$HTTP_HOST$REQUEST_URI" }
 }
-
-types {
-}
 ```
 Do not forget to change *<IPv4/6 address\>* to your server's address(es). If httpd should only listen on one protocol family, comment one of the two first *listen* options.
 
-Create the /var/www/htdocs/local/ folder and write the content of your robots.txt in /var/www/htdocs/local/robots.txt.
+Create the `/var/www/htdocs/local/` folder and write the content of your robots.txt in `/var/www/htdocs/local/robots.txt`.
 Check the configuration with `httpd -n`, if it is OK enable and start httpd (as root):
 ```
 rcctl enable httpd
@@ -108,7 +107,7 @@ rcctl start httpd
 
 #### acme-client
 acme-client is used to get SSL/TLS certificates from Let's Encrypt.
-Insert the following configuration in /etc/acme-client.conf:
+Insert the following configuration in `/etc/acme-client.conf`:
 ```
 #
 # $OpenBSD: acme-client.conf,v 1.4 2017/03/22 11:14:14 benno Exp $
@@ -129,7 +128,7 @@ domain <domain name> {
 }
 ```
 Replace *<domain name\>* by the domain name you'll use for your instance. As root, run `acme-client -n` to check the config, then `acme-client -ADv <domain name>` to create account and domain keys, and request a certificate for the first time.
-Make acme-client run everyday by adding it in /etc/daily.local. As root, run the following command: `echo "acme-client <domain name>" >> /etc/daily.local`.
+Make acme-client run everyday by adding it in `/etc/daily.local`. As root, run the following command: `echo "acme-client <domain name>" >> /etc/daily.local`.
 
 Relayd will look for certificates and keys based on the address it listens on (see next part), the easiest way to make them available to relayd is to create a link, as root run:
 ```
@@ -140,7 +139,7 @@ This will have to be done for each IPv4 and IPv6 address relayd listens on.
 
 #### relayd
 relayd will be used as the reverse proxy sitting in front of akkoma.
-Insert the following configuration in /etc/relayd.conf:
+Insert the following configuration in `/etc/relayd.conf`:
 ```
 # $OpenBSD: relayd.conf,v 1.4 2018/03/23 09:55:06 claudio Exp $
 
@@ -198,7 +197,7 @@ rcctl start relayd
 
 #### pf
 Enabling and configuring pf is highly recommended.
-In /etc/pf.conf, insert the following configuration:
+In `/etc/pf.conf`, insert the following configuration:
 ```
 # Macros
 if="<network interface>"
@@ -222,31 +221,30 @@ pass in quick on $if inet6 proto icmp6 to ($if) icmp6-type { echoreq unreach par
 pass in quick on $if proto tcp to ($if) port { http https } # relayd/httpd
 pass in quick on $if proto tcp from $authorized_ssh_clients to ($if) port ssh
 ```
-Replace *<network interface\>* by your server's network interface name (which you can get with ifconfig). Consider replacing the content of the authorized\_ssh\_clients macro by, for exemple, your home IP address, to avoid SSH connection attempts from bots.
+Replace *<network interface\>* by your server's network interface name (which you can get with ifconfig). Consider replacing the content of the `authorized_ssh_clients` macro by, for example, your home IP address, to avoid SSH connection attempts from bots.
 
 Check pf's configuration by running `pfctl -nf /etc/pf.conf`, load it with `pfctl -f /etc/pf.conf` and enable pf at boot with `rcctl enable pf`.
 
 #### Configure and start akkoma
-Enter a shell as \_akkoma (as root `su _akkoma -`) and enter akkoma's installation directory (`cd ~/akkoma/`).
+Enter a shell as `_akkoma` (as root `su _akkoma -`) and enter akkoma's installation directory (`cd ~/akkoma/`).
 
 Then follow the main installation guide:
 
   * run `mix deps.get`
   * run `MIX_ENV=prod mix pleroma.instance gen` and enter your instance's information when asked
-  * copy config/generated\_config.exs to config/prod.secret.exs. The default values should be sufficient but you should edit it and check that everything seems OK.
+  * copy `config/generated_config.exs` to `config/prod.secret.exs`. The default values should be sufficient but you should edit it and check that everything seems OK.
   * exit your current shell back to a root one and run `psql -U postgres -f /home/_akkoma/akkoma/config/setup_db.psql` to setup the database.
-  * return to a \_akkoma shell into akkoma's installation directory (`su _akkoma -;cd ~/akkoma`) and run `MIX_ENV=prod mix ecto.migrate`
+  * return to a `_akkoma` shell into akkoma's installation directory (`su _akkoma -;cd ~/akkoma`) and run `MIX_ENV=prod mix ecto.migrate`
 
-As \_akkoma in /home/\_akkoma/akkoma, you can now run `LC_ALL=en_US.UTF-8 MIX_ENV=prod mix phx.server` to start your instance.
+As `_akkoma` in `/home/_akkoma/akkoma`, you can now run `LC_ALL=en_US.UTF-8 MIX_ENV=prod mix phx.server` to start your instance.
 In another SSH session/tmux window, check that it is working properly by running `ftp -MVo - http://127.0.0.1:4000/api/v1/instance`, you should get json output. Double-check that *uri*'s value is your instance's domain name.
 
 ##### Starting akkoma at boot
 An rc script to automatically start akkoma at boot hasn't been written yet, it can be run in a tmux session (tmux is in base).
 
-
 #### Create administrative user
 
-If your instance is up and running, you can create your first user with administrative rights with the following command as the \_akkoma user.
+If your instance is up and running, you can create your first user with administrative rights with the following command as the `_akkoma` user.
 ```
 LC_ALL=en_US.UTF-8 MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress> --admin
 ```
diff --git a/docs/docs/installation/otp_en.md b/docs/docs/installation/otp_en.md
index ddbb66d96..058040e19 100644
--- a/docs/docs/installation/otp_en.md
+++ b/docs/docs/installation/otp_en.md
@@ -5,7 +5,7 @@
 This guide covers a installation using an OTP release. To install Akkoma from source, please check out the corresponding guide for your distro.
 
 ## Pre-requisites
-* A machine running Linux with GNU (e.g. Debian, Ubuntu) or musl (e.g. Alpine) libc and `x86_64`, `aarch64` or `armv7l` CPU, you have root access to. If you are not sure if it's compatible see [Detecting flavour section](#detecting-flavour) below
+* A machine running Linux with GNU (e.g. Debian, Ubuntu) or musl (e.g. Alpine) libc and an `x86_64` CPU you have root access to. If you are not sure if it's compatible see [Detecting flavour section](#detecting-flavour) below
 * For installing OTP releases on RedHat-based distros like Fedora and Centos Stream, please follow [this guide](./otp_redhat_en.md) instead.
 * A (sub)domain pointed to the machine
 
diff --git a/lib/pleroma/emoji/pack.ex b/lib/pleroma/emoji/pack.ex
index 2ca174c1f..f9b47a26b 100644
--- a/lib/pleroma/emoji/pack.ex
+++ b/lib/pleroma/emoji/pack.ex
@@ -252,7 +252,7 @@ def download(name, url, as) do
 
     with :ok <- validate_shareable_packs_available(uri),
          {:ok, remote_pack} <-
-           uri |> URI.merge("/api/v1/pleroma/emoji/pack?name=#{name}") |> http_get(),
+           uri |> URI.merge("/api/v1/pleroma/emoji/pack?name=#{URI.encode(name)}") |> http_get(),
          {:ok, %{sha: sha, url: url} = pack_info} <- fetch_pack_info(remote_pack, uri, name),
          {:ok, archive} <- download_archive(url, sha),
          pack <- copy_as(remote_pack, as || name),
@@ -593,7 +593,9 @@ defp fetch_pack_info(remote_pack, uri, name) do
         {:ok,
          %{
            sha: sha,
-           url: URI.merge(uri, "/api/v1/pleroma/emoji/packs/archive?name=#{name}") |> to_string()
+           url:
+             URI.merge(uri, "/api/v1/pleroma/emoji/packs/archive?name=#{URI.encode(name)}")
+             |> to_string()
          }}
 
       %{"fallback-src" => src, "fallback-src-sha256" => sha} when is_binary(src) ->
diff --git a/lib/pleroma/web/static_fe/static_fe_controller.ex b/lib/pleroma/web/static_fe/static_fe_controller.ex
index 8e454f7a0..56ee4e41e 100644
--- a/lib/pleroma/web/static_fe/static_fe_controller.ex
+++ b/lib/pleroma/web/static_fe/static_fe_controller.ex
@@ -25,7 +25,7 @@ def show(%{assigns: %{notice_id: notice_id}} = conn, _params) do
          true <- Visibility.is_public?(activity.object),
          {_, true} <- {:visible?, Visibility.visible_for_user?(activity, _reading_user = nil)},
          %User{} = user <- User.get_by_ap_id(activity.object.data["actor"]) do
-      meta = Metadata.build_tags(%{activity_id: notice_id, object: activity.object, user: user})
+      meta = Metadata.build_tags(%{url: activity.data["id"], object: activity.object, user: user})
 
       timeline =
         activity.object.data["context"]
diff --git a/lib/pleroma/web/twitter_api/controller.ex b/lib/pleroma/web/twitter_api/controller.ex
index 1e78ff2c1..c92ab63bc 100644
--- a/lib/pleroma/web/twitter_api/controller.ex
+++ b/lib/pleroma/web/twitter_api/controller.ex
@@ -18,10 +18,21 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
   action_fallback(:errors)
 
   def confirm_email(conn, %{"user_id" => uid, "token" => token}) do
-    with %User{} = user <- User.get_cached_by_id(uid),
-         true <- user.local and !user.is_confirmed and user.confirmation_token == token,
-         {:ok, _} <- User.confirm(user) do
-      redirect(conn, to: "/")
+    case User.get_cached_by_id(uid) do
+      %User{local: true, is_confirmed: false, confirmation_token: ^token} = user ->
+        case User.confirm(user) do
+          {:ok, _} ->
+            redirect(conn, to: "/")
+
+          {:error, _} ->
+            json_reply(conn, 400, "Unable to confirm")
+        end
+
+      %User{is_confirmed: true} ->
+        json_reply(conn, 400, "Already verified email")
+
+      _ ->
+        json_reply(conn, 400, "Couldn't verify email")
     end
   end
 
diff --git a/test/instance_static/emoji/test with spaces/blank.png b/test/instance_static/emoji/test with spaces/blank.png
new file mode 100644
index 000000000..8f50fa023
Binary files /dev/null and b/test/instance_static/emoji/test with spaces/blank.png differ
diff --git a/test/instance_static/emoji/test with spaces/blank2.png b/test/instance_static/emoji/test with spaces/blank2.png
new file mode 100644
index 000000000..8f50fa023
Binary files /dev/null and b/test/instance_static/emoji/test with spaces/blank2.png differ
diff --git a/test/instance_static/emoji/test with spaces/pack.json b/test/instance_static/emoji/test with spaces/pack.json
new file mode 100644
index 000000000..5b33fbb32
--- /dev/null
+++ b/test/instance_static/emoji/test with spaces/pack.json	
@@ -0,0 +1,12 @@
+{
+    "files": {
+        "blank": "blank.png",
+        "blank2": "blank2.png"
+    },
+    "pack": {
+        "description": "Test description",
+        "homepage": "https://pleroma.social",
+        "license": "Test license",
+        "share-files": true
+    }
+}
\ No newline at end of file
diff --git a/test/pleroma/web/pleroma_api/controllers/emoji_pack_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/emoji_pack_controller_test.exs
index b86418196..d6f9036e2 100644
--- a/test/pleroma/web/pleroma_api/controllers/emoji_pack_controller_test.exs
+++ b/test/pleroma/web/pleroma_api/controllers/emoji_pack_controller_test.exs
@@ -40,11 +40,11 @@ test "GET /api/v1/pleroma/emoji/packs", %{conn: conn} do
       |> get("/api/v1/pleroma/emoji/packs")
       |> json_response_and_validate_schema(200)
 
-    assert resp["count"] == 4
+    assert resp["count"] == 5
 
     assert resp["packs"]
            |> Map.keys()
-           |> length() == 4
+           |> length() == 5
 
     shared = resp["packs"]["test_pack"]
     assert shared["files"] == %{"blank" => "blank.png", "blank2" => "blank2.png"}
@@ -61,7 +61,7 @@ test "GET /api/v1/pleroma/emoji/packs", %{conn: conn} do
       |> get("/api/v1/pleroma/emoji/packs?page_size=1")
       |> json_response_and_validate_schema(200)
 
-    assert resp["count"] == 4
+    assert resp["count"] == 5
 
     packs = Map.keys(resp["packs"])
 
@@ -74,7 +74,7 @@ test "GET /api/v1/pleroma/emoji/packs", %{conn: conn} do
       |> get("/api/v1/pleroma/emoji/packs?page_size=1&page=2")
       |> json_response_and_validate_schema(200)
 
-    assert resp["count"] == 4
+    assert resp["count"] == 5
     packs = Map.keys(resp["packs"])
     assert length(packs) == 1
     [pack2] = packs
@@ -84,7 +84,7 @@ test "GET /api/v1/pleroma/emoji/packs", %{conn: conn} do
       |> get("/api/v1/pleroma/emoji/packs?page_size=1&page=3")
       |> json_response_and_validate_schema(200)
 
-    assert resp["count"] == 4
+    assert resp["count"] == 5
     packs = Map.keys(resp["packs"])
     assert length(packs) == 1
     [pack3] = packs
@@ -94,7 +94,7 @@ test "GET /api/v1/pleroma/emoji/packs", %{conn: conn} do
       |> get("/api/v1/pleroma/emoji/packs?page_size=1&page=4")
       |> json_response_and_validate_schema(200)
 
-    assert resp["count"] == 4
+    assert resp["count"] == 5
     packs = Map.keys(resp["packs"])
     assert length(packs) == 1
     [pack4] = packs
@@ -221,6 +221,24 @@ test "shared pack from remote and non shared from fallback-src", %{
           url: "https://nonshared-pack"
         } ->
           text(File.read!("#{@emoji_path}/test_pack_nonshared/nonshared.zip"))
+
+        %{
+          method: :get,
+          url: "https://example.com/api/v1/pleroma/emoji/pack?name=test%20with%20spaces"
+        } ->
+          conn
+          |> get("/api/v1/pleroma/emoji/pack?name=test%20with%20spaces")
+          |> json_response_and_validate_schema(200)
+          |> json()
+
+        %{
+          method: :get,
+          url: "https://example.com/api/v1/pleroma/emoji/packs/archive?name=test%20with%20spaces"
+        } ->
+          conn
+          |> get("/api/v1/pleroma/emoji/packs/archive?name=test%20with%20spaces")
+          |> response(200)
+          |> text()
       end)
 
       assert admin_conn
@@ -261,6 +279,18 @@ test "shared pack from remote and non shared from fallback-src", %{
              |> json_response_and_validate_schema(200) == "ok"
 
       refute File.exists?("#{@emoji_path}/test_pack_nonshared2")
+
+      assert admin_conn
+             |> put_req_header("content-type", "multipart/form-data")
+             |> post("/api/v1/pleroma/emoji/packs/download", %{
+               url: "https://example.com",
+               name: "test with spaces",
+               as: "test with spaces"
+             })
+             |> json_response_and_validate_schema(200) == "ok"
+
+      assert File.exists?("#{@emoji_path}/test with spaces/pack.json")
+      assert File.exists?("#{@emoji_path}/test with spaces/blank.png")
     end
 
     test "nonshareable instance", %{admin_conn: admin_conn} do
diff --git a/test/pleroma/web/twitter_api/controller_test.exs b/test/pleroma/web/twitter_api/controller_test.exs
index bca9e2dad..f3adc4b9f 100644
--- a/test/pleroma/web/twitter_api/controller_test.exs
+++ b/test/pleroma/web/twitter_api/controller_test.exs
@@ -38,16 +38,30 @@ test "it confirms the user account", %{conn: conn, user: user} do
       refute user.confirmation_token
     end
 
-    test "it returns 500 if user cannot be found by id", %{conn: conn, user: user} do
-      conn = get(conn, "/api/account/confirm_email/0/#{user.confirmation_token}")
+    test "confirmation is requested twice", %{conn: conn, user: user} do
+      conn = get(conn, "/api/account/confirm_email/#{user.id}/#{user.confirmation_token}")
+      assert 302 == conn.status
 
-      assert 500 == conn.status
+      conn = get(conn, "/api/account/confirm_email/#{user.id}/#{user.confirmation_token}")
+      assert 400 == conn.status
+      assert "Already verified email" == conn.resp_body
+
+      user = User.get_cached_by_id(user.id)
+
+      assert user.is_confirmed
+      refute user.confirmation_token
     end
 
-    test "it returns 500 if token is invalid", %{conn: conn, user: user} do
+    test "it returns 400 if user cannot be found by id", %{conn: conn, user: user} do
+      conn = get(conn, "/api/account/confirm_email/0/#{user.confirmation_token}")
+
+      assert 400 == conn.status
+    end
+
+    test "it returns 400 if token is invalid", %{conn: conn, user: user} do
       conn = get(conn, "/api/account/confirm_email/#{user.id}/wrong_token")
 
-      assert 500 == conn.status
+      assert 400 == conn.status
     end
   end