diff --git a/app/assets/images/fluffy-elephant-friend.png b/app/assets/images/fluffy-elephant-friend.png
index 11787e93603c796182f678880cddeb4f2787dd5c..f0df29927885c3daa2b7cfc380c31c5ce463e559 100644
Binary files a/app/assets/images/fluffy-elephant-friend.png and b/app/assets/images/fluffy-elephant-friend.png differ
diff --git a/app/assets/javascripts/components/features/getting_started/index.jsx b/app/assets/javascripts/components/features/getting_started/index.jsx
index 8253ad017b1ae80603d46b4ccf95a0d12406a941..d7a78d9cc2289c9f113978e81d87df6e34e46216 100644
--- a/app/assets/javascripts/components/features/getting_started/index.jsx
+++ b/app/assets/javascripts/components/features/getting_started/index.jsx
@@ -43,7 +43,7 @@ const GettingStarted = ({ intl, me }) => {
 
       <div className='scrollable optionally-scrollable' style={{ display: 'flex', flexDirection: 'column' }}>
         <div className='static-content getting-started'>
-          <p><FormattedMessage id='getting_started.open_source_notice' defaultMessage='Mastodon is open source software. You can contribute or report issues on github at {github}. {apps}.' values={{ github: <a href="https://github.com/tootsuite/mastodon" target="_blank">tootsuite/mastodon</a>, apps: <a href="https://github.com/tootsuite/mastodon/blob/master/docs/Using-Mastodon/Apps.md" target="_blank"><FormattedMessage id='getting_started.apps' defaultMessage='Various apps are available' /></a> }} /></p>
+          <p><FormattedMessage id='getting_started.open_source_notice' defaultMessage='Mastodon is open source software. You can contribute or report issues on GitHub at {github}. {apps}.' values={{ github: <a href="https://github.com/tootsuite/mastodon" target="_blank">tootsuite/mastodon</a>, apps: <a href="https://github.com/tootsuite/mastodon/blob/master/docs/Using-Mastodon/Apps.md" target="_blank"><FormattedMessage id='getting_started.apps' defaultMessage='Various apps are available' /></a> }} /></p>
         </div>
       </div>
     </Column>
diff --git a/app/assets/javascripts/components/locales/en.jsx b/app/assets/javascripts/components/locales/en.jsx
index 2d3360b6b54c6c00da21a7cfb0f08cd961f5ce17..53e2898eb64191cc72a3ae6c0d1de57e00d69414 100644
--- a/app/assets/javascripts/components/locales/en.jsx
+++ b/app/assets/javascripts/components/locales/en.jsx
@@ -25,7 +25,7 @@ const en = {
   "getting_started.heading": "Getting started",
   "getting_started.about_addressing": "You can follow people if you know their username and the domain they are on by entering an e-mail-esque address into the search form.",
   "getting_started.about_shortcuts": "If the target user is on the same domain as you, just the username will work. The same rule applies to mentioning people in statuses.",
-  "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on github at {github}. {apps}.",
+  "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}. {apps}.",
   "column.home": "Home",
   "column.community": "Local timeline",
   "column.public": "Federated timeline",
diff --git a/app/controllers/admin/domain_blocks_controller.rb b/app/controllers/admin/domain_blocks_controller.rb
index e362957e7395c05f408181f20e7f5ec0f5402b67..1f443284784ba012df896d240657e03d58fcd84d 100644
--- a/app/controllers/admin/domain_blocks_controller.rb
+++ b/app/controllers/admin/domain_blocks_controller.rb
@@ -9,6 +9,24 @@ class Admin::DomainBlocksController < ApplicationController
     @blocks = DomainBlock.paginate(page: params[:page], per_page: 40)
   end
 
+  def new
+    @domain_block = DomainBlock.new
+  end
+
   def create
+    @domain_block = DomainBlock.new(resource_params)
+
+    if @domain_block.save
+      DomainBlockWorker.perform_async(@domain_block.id)
+      redirect_to admin_domain_blocks_path, notice: 'Domain block is now being processed'
+    else
+      render action: :new
+    end
+  end
+
+  private
+
+  def resource_params
+    params.require(:domain_block).permit(:domain, :severity)
   end
 end
diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb
index 0117a18eea25945a8426da35c0c155910261ff5a..2b3b1809fdd4f67d4c52ee3e1e0f43f2b056b5f7 100644
--- a/app/controllers/admin/reports_controller.rb
+++ b/app/controllers/admin/reports_controller.rb
@@ -16,19 +16,19 @@ class Admin::ReportsController < ApplicationController
   end
 
   def resolve
-    @report.update(action_taken: true)
+    @report.update(action_taken: true, action_taken_by_account_id: current_account.id)
     redirect_to admin_report_path(@report)
   end
 
   def suspend
     Admin::SuspensionWorker.perform_async(@report.target_account.id)
-    @report.update(action_taken: true)
+    Report.unresolved.where(target_account: @report.target_account).update_all(action_taken: true, action_taken_by_account_id: current_account.id)
     redirect_to admin_report_path(@report)
   end
 
   def silence
     @report.target_account.update(silenced: true)
-    @report.update(action_taken: true)
+    Report.unresolved.where(target_account: @report.target_account).update_all(action_taken: true, action_taken_by_account_id: current_account.id)
     redirect_to admin_report_path(@report)
   end
 
diff --git a/app/controllers/api/v1/apps_controller.rb b/app/controllers/api/v1/apps_controller.rb
index ca9dd0b7eea90eb25285f1154332915f58558542..2ec7280af05d00c87fca8943ca62ab279f9931d0 100644
--- a/app/controllers/api/v1/apps_controller.rb
+++ b/app/controllers/api/v1/apps_controller.rb
@@ -4,6 +4,12 @@ class Api::V1::AppsController < ApiController
   respond_to :json
 
   def create
-    @app = Doorkeeper::Application.create!(name: params[:client_name], redirect_uri: params[:redirect_uris], scopes: (params[:scopes] || Doorkeeper.configuration.default_scopes), website: params[:website])
+    @app = Doorkeeper::Application.create!(name: app_params[:client_name], redirect_uri: app_params[:redirect_uris], scopes: (app_params[:scopes] || Doorkeeper.configuration.default_scopes), website: app_params[:website])
+  end
+
+  private
+
+  def app_params
+    params.permit(:client_name, :redirect_uris, :scopes, :website)
   end
 end
diff --git a/app/controllers/api/v1/follows_controller.rb b/app/controllers/api/v1/follows_controller.rb
index c22dacbaab794ce78f32dc44488fa9d0e8415be0..7c0f44f038313ca07a807cb8caec2731aa5a4d2f 100644
--- a/app/controllers/api/v1/follows_controller.rb
+++ b/app/controllers/api/v1/follows_controller.rb
@@ -7,7 +7,7 @@ class Api::V1::FollowsController < ApiController
   respond_to :json
 
   def create
-    raise ActiveRecord::RecordNotFound if params[:uri].blank?
+    raise ActiveRecord::RecordNotFound if follow_params[:uri].blank?
 
     @account = FollowService.new.call(current_user.account, target_uri).try(:target_account)
     render action: :show
@@ -16,6 +16,10 @@ class Api::V1::FollowsController < ApiController
   private
 
   def target_uri
-    params[:uri].strip.gsub(/\A@/, '')
+    follow_params[:uri].strip.gsub(/\A@/, '')
+  end
+
+  def follow_params
+    params.permit(:uri)
   end
 end
diff --git a/app/controllers/api/v1/media_controller.rb b/app/controllers/api/v1/media_controller.rb
index f8139ade7719a9d9d927aa23b93c130e58d99569..aed3578d7e827d7f327ed2598c7e5d13fecd6346 100644
--- a/app/controllers/api/v1/media_controller.rb
+++ b/app/controllers/api/v1/media_controller.rb
@@ -10,10 +10,16 @@ class Api::V1::MediaController < ApiController
   respond_to :json
 
   def create
-    @media = MediaAttachment.create!(account: current_user.account, file: params[:file])
+    @media = MediaAttachment.create!(account: current_user.account, file: media_params[:file])
   rescue Paperclip::Errors::NotIdentifiedByImageMagickError
     render json: { error: 'File type of uploaded media could not be verified' }, status: 422
   rescue Paperclip::Error
     render json: { error: 'Error processing thumbnail for uploaded media' }, status: 500
   end
+
+  private
+
+  def media_params
+    params.permit(:file)
+  end
 end
diff --git a/app/controllers/api/v1/reports_controller.rb b/app/controllers/api/v1/reports_controller.rb
index 46bdddbc1487a5104b852446d61627c2c74794c3..f83c573cb5fbaeaa798c02581cbe48b338125f1a 100644
--- a/app/controllers/api/v1/reports_controller.rb
+++ b/app/controllers/api/v1/reports_controller.rb
@@ -12,13 +12,19 @@ class Api::V1::ReportsController < ApiController
   end
 
   def create
-    status_ids = params[:status_ids].is_a?(Enumerable) ? params[:status_ids] : [params[:status_ids]]
+    status_ids = report_params[:status_ids].is_a?(Enumerable) ? report_params[:status_ids] : [report_params[:status_ids]]
 
     @report = Report.create!(account: current_account,
-                             target_account: Account.find(params[:account_id]),
+                             target_account: Account.find(report_params[:account_id]),
                              status_ids: Status.find(status_ids).pluck(:id),
-                             comment: params[:comment])
+                             comment: report_params[:comment])
 
     render :show
   end
+
+  private
+
+  def report_params
+    params.permit(:account_id, :comment, status_ids: [])
+  end
 end
diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb
index 024258c0e1551c62094fe1459ae8f8b1ad11dccc..4ece7e702819f403d0fa3de8ebc142e03f299204 100644
--- a/app/controllers/api/v1/statuses_controller.rb
+++ b/app/controllers/api/v1/statuses_controller.rb
@@ -62,11 +62,11 @@ class Api::V1::StatusesController < ApiController
   end
 
   def create
-    @status = PostStatusService.new.call(current_user.account, params[:status], params[:in_reply_to_id].blank? ? nil : Status.find(params[:in_reply_to_id]), media_ids: params[:media_ids],
-                                                                                                                                                             sensitive: params[:sensitive],
-                                                                                                                                                             spoiler_text: params[:spoiler_text],
-                                                                                                                                                             visibility: params[:visibility],
-                                                                                                                                                             application: doorkeeper_token.application)
+    @status = PostStatusService.new.call(current_user.account, status_params[:status], status_params[:in_reply_to_id].blank? ? nil : Status.find(status_params[:in_reply_to_id]), media_ids: status_params[:media_ids],
+                                                                                                                                                                                  sensitive: status_params[:sensitive],
+                                                                                                                                                                                  spoiler_text: status_params[:spoiler_text],
+                                                                                                                                                                                  visibility: status_params[:visibility],
+                                                                                                                                                                                  application: doorkeeper_token.application)
     render action: :show
   end
 
@@ -111,4 +111,8 @@ class Api::V1::StatusesController < ApiController
     @status = Status.find(params[:id])
     raise ActiveRecord::RecordNotFound unless @status.permitted?(current_account)
   end
+
+  def status_params
+    params.permit(:status, :in_reply_to_id, :sensitive, :spoiler_text, :visibility, media_ids: [])
+  end
 end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index ef9364897c911ded164cba8454fa2a5e7b7acd12..c06142fd43ac211fc597f15038186524d5e18b88 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -39,7 +39,14 @@ class ApplicationController < ActionController::Base
   end
 
   def set_user_activity
-    current_user.touch(:current_sign_in_at) if !current_user.nil? && (current_user.current_sign_in_at.nil? || current_user.current_sign_in_at < 24.hours.ago)
+    return unless !current_user.nil? && (current_user.current_sign_in_at.nil? || current_user.current_sign_in_at < 24.hours.ago)
+
+    # Mark user as signed-in today
+    current_user.update_tracked_fields(request)
+
+    # If the sign in is after a two week break, we need to regenerate their feed
+    RegenerationWorker.perform_async(current_user.account_id) if current_user.last_sign_in_at < 14.days.ago
+    return
   end
 
   def check_suspension
diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb
index feaad04f6b6543a63702b5dbc56b4cb868f4707d..7c25266d81356fe66881c6bbf52e4284682a1bae 100644
--- a/app/controllers/oauth/authorizations_controller.rb
+++ b/app/controllers/oauth/authorizations_controller.rb
@@ -3,6 +3,7 @@
 class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
   skip_before_action :authenticate_resource_owner!
 
+  before_action :set_locale
   before_action :store_current_location
   before_action :authenticate_resource_owner!
 
@@ -11,4 +12,10 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
   def store_current_location
     store_location_for(:user, request.url)
   end
+
+  def set_locale
+    I18n.locale = current_user.try(:locale) || I18n.default_locale
+  rescue I18n::InvalidLocale
+    I18n.locale = I18n.default_locale
+  end
 end
diff --git a/app/lib/exceptions.rb b/app/lib/exceptions.rb
index 200da9fe16a1feac0449641e9f17c7e5342d4176..9bc802c12074f0bf5aa9254fe53255078f783d0e 100644
--- a/app/lib/exceptions.rb
+++ b/app/lib/exceptions.rb
@@ -4,4 +4,5 @@ module Mastodon
   class Error < StandardError; end
   class NotPermittedError < Error; end
   class ValidationError < Error; end
+  class RaceConditionError < Error; end
 end
diff --git a/app/models/feed.rb b/app/models/feed.rb
index 5e1905e15244be8332f7257d714460c43fd40cf0..3cbc160a06d1e87cd37c9d21113c3d73cae8c5ac 100644
--- a/app/models/feed.rb
+++ b/app/models/feed.rb
@@ -10,17 +10,9 @@ class Feed
     max_id     = '+inf' if max_id.blank?
     since_id   = '-inf' if since_id.blank?
     unhydrated = redis.zrevrangebyscore(key, "(#{max_id}", "(#{since_id}", limit: [0, limit], with_scores: true).map(&:last).map(&:to_i)
+    status_map = Status.where(id: unhydrated).cache_ids.map { |s| [s.id, s] }.to_h
 
-    # If we're after most recent items and none are there, we need to precompute the feed
-    if unhydrated.empty? && max_id == '+inf' && since_id == '-inf'
-      RegenerationWorker.perform_async(@account.id, @type)
-      @statuses = Status.send("as_#{@type}_timeline", @account).cache_ids.paginate_by_max_id(limit, nil, nil)
-    else
-      status_map = Status.where(id: unhydrated).cache_ids.map { |s| [s.id, s] }.to_h
-      @statuses  = unhydrated.map { |id| status_map[id] }.compact
-    end
-
-    @statuses
+    unhydrated.map { |id| status_map[id] }.compact
   end
 
   private
diff --git a/app/models/report.rb b/app/models/report.rb
index 05dc8cff1954533945c217a459251bdd74517457..fd8e46aaccf6ffe206b962b230a0f618738a0810 100644
--- a/app/models/report.rb
+++ b/app/models/report.rb
@@ -3,6 +3,7 @@
 class Report < ApplicationRecord
   belongs_to :account
   belongs_to :target_account, class_name: 'Account'
+  belongs_to :action_taken_by_account, class_name: 'Account'
 
   scope :unresolved, -> { where(action_taken: false) }
   scope :resolved,   -> { where(action_taken: true) }
diff --git a/app/models/status.rb b/app/models/status.rb
index 81b26fd1459ee7464fec62155960956eb286b4a2..daf1285720fd17bc0ef52d73630506b25337d453 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -188,7 +188,7 @@ class Status < ApplicationRecord
   end
 
   before_validation do
-    text.strip!
+    text&.strip!
     spoiler_text&.strip!
 
     self.reply                  = !(in_reply_to_id.nil? && thread.nil?) unless reply
diff --git a/app/services/block_domain_service.rb b/app/services/block_domain_service.rb
index 9518b1fcf2860e855161a6051f70b7cc8f239530..6c131bd3410c35b05990ff2297476e89424e0a9c 100644
--- a/app/services/block_domain_service.rb
+++ b/app/services/block_domain_service.rb
@@ -1,13 +1,11 @@
 # frozen_string_literal: true
 
 class BlockDomainService < BaseService
-  def call(domain, severity)
-    DomainBlock.where(domain: domain).first_or_create!(domain: domain, severity: severity)
-
-    if severity == :silence
-      Account.where(domain: domain).update_all(silenced: true)
+  def call(domain_block)
+    if domain_block.silence?
+      Account.where(domain: domain_block.domain).update_all(silenced: true)
     else
-      Account.where(domain: domain).find_each do |account|
+      Account.where(domain: domain_block.domain).find_each do |account|
         account.subscription(api_subscription_url(account.id)).unsubscribe if account.subscribed?
         SuspendAccountService.new.call(account)
       end
diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb
index 402b84b2f873b6cec7475f73e012d8c519f1059d..df404cbef174e1597d6a29c050d86bda8b0adcbe 100644
--- a/app/services/fan_out_on_write_service.rb
+++ b/app/services/fan_out_on_write_service.rb
@@ -4,6 +4,8 @@ class FanOutOnWriteService < BaseService
   # Push a status into home and mentions feeds
   # @param [Status] status
   def call(status)
+    raise Mastodon::RaceConditionError if status.visibility.nil?
+
     deliver_to_self(status) if status.account.local?
 
     if status.direct_visibility?
diff --git a/app/views/admin/domain_blocks/index.html.haml b/app/views/admin/domain_blocks/index.html.haml
index dbaeb4716450aa760008b451def7cfbb9d559549..eb7894b86cc9c731f7eb4f1d26b70bbc69a7d39a 100644
--- a/app/views/admin/domain_blocks/index.html.haml
+++ b/app/views/admin/domain_blocks/index.html.haml
@@ -14,3 +14,4 @@
         %td= block.severity
 
 = will_paginate @blocks, pagination_options
+= link_to 'Add new', new_admin_domain_block_path, class: 'button'
diff --git a/app/views/admin/domain_blocks/new.html.haml b/app/views/admin/domain_blocks/new.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..fbd39d6cfa99d9b47498720f7eb0d99b55f5bff0
--- /dev/null
+++ b/app/views/admin/domain_blocks/new.html.haml
@@ -0,0 +1,18 @@
+- content_for :page_title do
+  New domain block
+
+= simple_form_for @domain_block, url: admin_domain_blocks_path do |f|
+  = render 'shared/error_messages', object: @domain_block
+
+  %p.hint The domain block will not prevent creation of account entries in the database, but will retroactively and automatically apply specific moderation methods on those accounts.
+
+  = f.input :domain, placeholder: 'Domain'
+  = f.input :severity, collection: DomainBlock.severities.keys, wrapper: :with_label, include_blank: false
+
+  %p.hint
+    %strong Silence
+    will make the account's posts invisible to anyone who isn't following them.
+    %strong Suspend
+    will remove all of the account's content, media, and profile data.
+  .actions
+    = f.button :button, 'Create block', type: :submit
diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml
index 8a5414cef3db618a412bdbfaa5facfac72302893..839259dc27a4541a1156e43726867f53546cbc76 100644
--- a/app/views/admin/reports/index.html.haml
+++ b/app/views/admin/reports/index.html.haml
@@ -8,20 +8,25 @@
       %li= filter_link_to 'Unresolved', action_taken: nil
       %li= filter_link_to 'Resolved', action_taken: '1'
 
-%table.table
-  %thead
-    %tr
-      %th ID
-      %th Target
-      %th Reported by
-      %th Comment
-      %th
-  %tbody
-    - @reports.each do |report|
+= form_tag do
+
+  %table.table
+    %thead
       %tr
-        %td= "##{report.id}"
-        %td= link_to report.target_account.acct, admin_account_path(report.target_account.id)
-        %td= link_to report.account.acct, admin_account_path(report.account.id)
-        %td= truncate(report.comment, length: 30, separator: ' ')
-        %td= table_link_to 'circle', 'View', admin_report_path(report)
+        %th
+        %th ID
+        %th Target
+        %th Reported by
+        %th Comment
+        %th
+    %tbody
+      - @reports.each do |report|
+        %tr
+          %td= check_box_tag 'select', report.id
+          %td= "##{report.id}"
+          %td= link_to report.target_account.acct, admin_account_path(report.target_account.id)
+          %td= link_to report.account.acct, admin_account_path(report.account.id)
+          %td= truncate(report.comment, length: 30, separator: ' ')
+          %td= table_link_to 'circle', 'View', admin_report_path(report)
+
 = will_paginate @reports, pagination_options
diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml
index 74cac016ddf7e4870f2de8e863cd78f6776eddd1..caa8415dffff963c5c9e7ea1d6a6e926e2783b7b 100644
--- a/app/views/admin/reports/show.html.haml
+++ b/app/views/admin/reports/show.html.haml
@@ -27,7 +27,7 @@
         = link_to remove_admin_report_path(@report, status_id: status.id), method: :post, class: 'icon-button', style: 'font-size: 24px; width: 24px; height: 24px', title: 'Delete' do
           = fa_icon 'trash'
 
-- unless @report.action_taken?
+- if !@report.action_taken?
   %hr/
 
   %div{ style: 'overflow: hidden' }
@@ -36,3 +36,9 @@
       = link_to 'Suspend account', suspend_admin_report_path(@report), method: :post, class: 'button'
     %div{ style: 'float: left' }
       = link_to 'Mark as resolved', resolve_admin_report_path(@report), method: :post, class: 'button'
+- elsif !@report.action_taken_by_account.nil?
+  %hr/
+
+  %p
+    %strong Action taken by:
+    = @report.action_taken_by_account.acct
diff --git a/app/workers/after_remote_follow_request_worker.rb b/app/workers/after_remote_follow_request_worker.rb
index f1d6869cc74dcfa4e2bc393d93d99ed96b0a5218..1f2db3061571c9bdf7a473d598828d74b408af18 100644
--- a/app/workers/after_remote_follow_request_worker.rb
+++ b/app/workers/after_remote_follow_request_worker.rb
@@ -3,7 +3,7 @@
 class AfterRemoteFollowRequestWorker
   include Sidekiq::Worker
 
-  sidekiq_options retry: 5
+  sidekiq_options queue: 'pull', retry: 5
 
   def perform(follow_request_id)
     follow_request  = FollowRequest.find(follow_request_id)
diff --git a/app/workers/after_remote_follow_worker.rb b/app/workers/after_remote_follow_worker.rb
index 0d04456a94209581c992b18cfc18ed623865cb0e..bdd2c2a91df89b2d849ff211d6ec11267fbf9c90 100644
--- a/app/workers/after_remote_follow_worker.rb
+++ b/app/workers/after_remote_follow_worker.rb
@@ -3,7 +3,7 @@
 class AfterRemoteFollowWorker
   include Sidekiq::Worker
 
-  sidekiq_options retry: 5
+  sidekiq_options queue: 'pull', retry: 5
 
   def perform(follow_id)
     follow          = Follow.find(follow_id)
diff --git a/app/workers/domain_block_worker.rb b/app/workers/domain_block_worker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8844778291d41bf96b100f3f09e83a63bd8c94a9
--- /dev/null
+++ b/app/workers/domain_block_worker.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class DomainBlockWorker
+  include Sidekiq::Worker
+
+  def perform(domain_block_id)
+    BlockDomainService.new.call(DomainBlock.find(domain_block_id))
+  rescue ActiveRecord::RecordNotFound
+    true
+  end
+end
diff --git a/app/workers/import_worker.rb b/app/workers/import_worker.rb
index a3ae2a85a4987edbf7c7adc7ab5162ccbe4c6559..7cf29fb53c58d8db5f98b9693aa767856aad9f4c 100644
--- a/app/workers/import_worker.rb
+++ b/app/workers/import_worker.rb
@@ -5,7 +5,7 @@ require 'csv'
 class ImportWorker
   include Sidekiq::Worker
 
-  sidekiq_options retry: false
+  sidekiq_options queue: 'pull', retry: false
 
   def perform(import_id)
     import = Import.find(import_id)
diff --git a/app/workers/link_crawl_worker.rb b/app/workers/link_crawl_worker.rb
index af3394b8b33e71f83bb05d7ae42cf776b30add79..834b0088bdfd1993eb4cbbf68cbec2ac9d3cd624 100644
--- a/app/workers/link_crawl_worker.rb
+++ b/app/workers/link_crawl_worker.rb
@@ -3,7 +3,7 @@
 class LinkCrawlWorker
   include Sidekiq::Worker
 
-  sidekiq_options retry: false
+  sidekiq_options queue: 'pull', retry: false
 
   def perform(status_id)
     FetchLinkCardService.new.call(Status.find(status_id))
diff --git a/app/workers/merge_worker.rb b/app/workers/merge_worker.rb
index 0f288f43fe976a44a26b398297b3dc360dd1f83c..d745cb99c7b9aada475223d9250a0315dc0c6c5b 100644
--- a/app/workers/merge_worker.rb
+++ b/app/workers/merge_worker.rb
@@ -3,6 +3,8 @@
 class MergeWorker
   include Sidekiq::Worker
 
+  sidekiq_options queue: 'pull'
+
   def perform(from_account_id, into_account_id)
     FeedManager.instance.merge_into_timeline(Account.find(from_account_id), Account.find(into_account_id))
   end
diff --git a/app/workers/notification_worker.rb b/app/workers/notification_worker.rb
index 1a2faefd8e08da9ad3ccfee4055a4eea08c79641..da1d6ab455cbba498997f807bdd266857a26b702 100644
--- a/app/workers/notification_worker.rb
+++ b/app/workers/notification_worker.rb
@@ -3,7 +3,7 @@
 class NotificationWorker
   include Sidekiq::Worker
 
-  sidekiq_options retry: 5
+  sidekiq_options queue: 'push', retry: 5
 
   def perform(xml, source_account_id, target_account_id)
     SendInteractionService.new.call(xml, Account.find(source_account_id), Account.find(target_account_id))
diff --git a/app/workers/processing_worker.rb b/app/workers/processing_worker.rb
index 5df404bcc9b7bb3e653bbfb20d22482b9457c398..4a467d92446a2fc68589b9ab2e82ffaace7e0617 100644
--- a/app/workers/processing_worker.rb
+++ b/app/workers/processing_worker.rb
@@ -3,7 +3,7 @@
 class ProcessingWorker
   include Sidekiq::Worker
 
-  sidekiq_options backtrace: true
+  sidekiq_options queue: 'pull', backtrace: true
 
   def perform(account_id, body)
     ProcessFeedService.new.call(body, Account.find(account_id))
diff --git a/app/workers/regeneration_worker.rb b/app/workers/regeneration_worker.rb
index 3aece0ba28a63061bc5440f514111aaf74a6e8af..82665b581f91a06c55731cac35cca17f9fbd7f77 100644
--- a/app/workers/regeneration_worker.rb
+++ b/app/workers/regeneration_worker.rb
@@ -3,7 +3,9 @@
 class RegenerationWorker
   include Sidekiq::Worker
 
-  def perform(account_id, timeline_type)
-    PrecomputeFeedService.new.call(timeline_type, Account.find(account_id))
+  sidekiq_options queue: 'pull', backtrace: true
+
+  def perform(account_id, _ = :home)
+    PrecomputeFeedService.new.call(:home, Account.find(account_id))
   end
 end
diff --git a/app/workers/salmon_worker.rb b/app/workers/salmon_worker.rb
index fc95ce47fff8477336571dfc4bcba205df70810a..2888b574b66093f47a8ccaadc51928b35f9ab021 100644
--- a/app/workers/salmon_worker.rb
+++ b/app/workers/salmon_worker.rb
@@ -3,7 +3,7 @@
 class SalmonWorker
   include Sidekiq::Worker
 
-  sidekiq_options backtrace: true
+  sidekiq_options queue: 'pull', backtrace: true
 
   def perform(account_id, body)
     ProcessInteractionService.new.call(body, Account.find(account_id))
diff --git a/app/workers/thread_resolve_worker.rb b/app/workers/thread_resolve_worker.rb
index 593edd032f2e62c06019912908c95f8cb28fab74..38287e8e64b683ae1dfa1538ffe165accd7d1aa8 100644
--- a/app/workers/thread_resolve_worker.rb
+++ b/app/workers/thread_resolve_worker.rb
@@ -3,7 +3,7 @@
 class ThreadResolveWorker
   include Sidekiq::Worker
 
-  sidekiq_options retry: false
+  sidekiq_options queue: 'pull', retry: false
 
   def perform(child_status_id, parent_url)
     child_status  = Status.find(child_status_id)
diff --git a/app/workers/unmerge_worker.rb b/app/workers/unmerge_worker.rb
index dbf7243de752584c1304346f9572f2d48fca1cce..ea6aacebf6df28fc7bad92bb8fc455661a16155e 100644
--- a/app/workers/unmerge_worker.rb
+++ b/app/workers/unmerge_worker.rb
@@ -3,6 +3,8 @@
 class UnmergeWorker
   include Sidekiq::Worker
 
+  sidekiq_options queue: 'pull'
+
   def perform(from_account_id, into_account_id)
     FeedManager.instance.unmerge_from_timeline(Account.find(from_account_id), Account.find(into_account_id))
   end
diff --git a/config/locales/devise.no.yml b/config/locales/devise.no.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8b650e548ff61ae291fcd31e80b010588b190f60
--- /dev/null
+++ b/config/locales/devise.no.yml
@@ -0,0 +1,61 @@
+---
+'no':
+  devise:
+    confirmations:
+      confirmed: Epostaddressen din er blitt bekreftet.
+      send_instructions: Du vil motta en epost med instruksjoner for hvordan bekrefte din epostaddresse om noen få minutter.
+      send_paranoid_instructions: Hvis din epostaddresse finnes i vår database vil du motta en epost med instruksjoner for hvordan bekrefte din epost om noen få minutter.
+    failure:
+      already_authenticated: Du er allerede innlogget.
+      inactive: Din konto er ikke blitt aktivert ennå.
+      invalid: Ugyldig %{authentication_keys} eller passord.
+      last_attempt: Du har ett forsøk igjen før kontoen din bli låst.
+      locked: Din konto er låst.
+      not_found_in_database: Ugyldig %{authentication_keys} eller passord.
+      timeout: Sesjonen din løp ut på tid. Logg inn på nytt for å fortsette.
+      unauthenticated: Du må logge inn eller registrere deg før du kan fortsette.
+      unconfirmed: Du må bekrefte epostadressen din før du kan fortsette.
+    mailer:
+      confirmation_instructions:
+        subject: 'Mastodon: Instruksjoner for å bekrefte epostadresse'
+      password_change:
+        subject: 'Mastodon: Passord endret'
+      reset_password_instructions:
+        subject: 'Mastodon: Hvordan nullstille passord?'
+      unlock_instructions:
+        subject: 'Mastodon: Instruksjoner for å gjenåpne konto'
+    omniauth_callbacks:
+      failure: Kunne ikke autentisere deg fra %{kind} fordi "%{reason}".
+      success: Vellykket autentisering fra %{kind}.
+    passwords:
+      no_token: Du har ingen tilgang til denne siden så lenge du ikke kommer fra en epost om nullstilling av passord. Hvis du kommer fra en passordnullstilling epost, dobbelsjekk at du brukte hele URLen.
+      send_instructions: Du vil motta en epost med instruksjoner for å nullstille passordet ditt om noen få minutter.
+      send_paranoid_instructions: Hvis epostadressen din finnes i databasen vår vil du motta en instruksjonsmail om passord nullstilling om noen få minutter.
+      updated: Passordet ditt har blitt endret. Du er nå logget inn.
+      updated_not_active: Passordet ditt har blitt endret.
+    registrations:
+      destroyed: Adjø! Kontoen din har blitt avsluttet. Vi håper at vi ser deg igjen snart.
+      signed_up: Velkommen! Registrasjonen var vellykket.
+      signed_up_but_inactive: Registrasjonen var vellykket. Vi kunne dessverre ikke logge deg inn fordi kontoen din ennå ikke har blitt aktivert.
+      signed_up_but_locked: Registrasjonen var vellykket. Vi kunne dessverre ikke logge deg inn fordi kontoen din har blitt låst.
+      signed_up_but_unconfirmed: En epostmelding med en bekreftelseslink har blitt sendt til din adresse. Klikk på linken i eposten for å aktivere kontoen din.
+      update_needs_confirmation: Du har oppdatert kontoen din, men vi må bekrefte din nye epostadresse. Sjekk eposten din og følg bekreftelseslinken for å bekrefte din nye epostadresse.
+      updated: Kontoen din ble oppdatert.
+    sessions:
+      already_signed_out: Logget ut.
+      signed_in: Logget inn.
+      signed_out: Logget ut.
+    unlocks:
+      send_instructions: Du vil motta en epost med instruksjoner for å åpne kontoen din om noen få minutter.
+      send_paranoid_instructions: Hvis kontoen din eksisterer vil du motta en epost med instruksjoner for å åpne kontoen din om noen få minutter.
+      unlocked: Kontoen din ble åpnet uten problemer. Logg på for å fortsette.
+  errors:
+    messages:
+      already_confirmed: har allerede blitt bekreftet, prøv å logg på istedet.
+      confirmation_period_expired: må bekreftes innen %{period}. Spør om en ny bekreftelsesmail istedet.
+      expired: har utløpt, spør om en ny en istedet
+      not_found: ikke funnet
+      not_locked: var ikke låst
+      not_saved:
+        one: '1 feil hindret denne %{resource} fra å bli lagret:'
+        other: "%{count} feil hindret denne %{resource} fra å bli lagret:"
diff --git a/config/locales/doorkeeper.no.yml b/config/locales/doorkeeper.no.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f149f53e0194f0320d66d358bc504ef6bbbaa685
--- /dev/null
+++ b/config/locales/doorkeeper.no.yml
@@ -0,0 +1,113 @@
+---
+'no':
+  activerecord:
+    attributes:
+      doorkeeper/application:
+        name: Navn
+        redirect_uri: Omdirigerings-URI
+    errors:
+      models:
+        doorkeeper/application:
+          attributes:
+            redirect_uri:
+              fragment_present: kan ikke inneholde ett fragment.
+              invalid_uri: må være en gyldig URI.
+              relative_uri: må være en absolutt URI.
+              secured_uri: må være en HTTPS/SSL URI.
+  doorkeeper:
+    applications:
+      buttons:
+        authorize: Autoriser
+        cancel: Avbryt
+        destroy: Ødelegg
+        edit: Endre
+        submit: Send inn
+      confirmations:
+        destroy: Er du sikker?
+      edit:
+        title: Endre applikasjon
+      form:
+        error: Whoops! Sjekk skjemaet ditt for mulige feil
+      help:
+        native_redirect_uri: Bruk %{native_redirect_uri} for lokale tester
+        redirect_uri: Bruk en linje per URI
+        scopes: Adskill omfang med mellomrom. La det være blankt for å bruke standard omfang.
+      index:
+        callback_url: Callback URL
+        name: Navn
+        new: Ny Applikasjon
+        title: Dine applikasjoner
+      new:
+        title: Ny Applikasjoner
+      show:
+        actions: Operasjoner
+        application_id: Applikasjon Id
+        callback_urls: Callback urls
+        scopes: Omfang
+        secret: Hemmelighet
+        title: 'Applikasjon: %{name}'
+    authorizations:
+      buttons:
+        authorize: Autoriser
+        deny: Avvis
+      error:
+        title: En feil oppsto
+      new:
+        able_to: Den vil ha mulighet til
+        prompt: Applikasjon %{client_name} spør om tilgang til din konto
+        title: Autorisasjon påkrevd
+      show:
+        title: Autoriserings kode
+    authorized_applications:
+      buttons:
+        revoke: Opphev
+      confirmations:
+        revoke: Opphev?
+      index:
+        application: Applikasjon
+        created_at: Autorisert
+        date_format: "%Y-%m-%d %H:%M:%S"
+        scopes: Omfang
+        title: Dine autoriserte applikasjoner
+    errors:
+      messages:
+        access_denied: Ressurseieren eller autoriserings tjeneren avviste forespørslen.
+        credential_flow_not_configured: Ressurseiers passord flyt feilet på grunn av at Doorkeeper.configure.resource_owner_from_credentials ikke var konfigurert.
+        invalid_client: Klient autentisering feilet på grunn av ukjent klient, ingen autentisering inkludert eller autentiserings metode som ikke er støttet.
+        invalid_grant: Autoriseringen er ugyldig, utløpt, opphevet, stemmer ikke overens med omdirigerings-URIen eller var utstedt til en annen klient.
+        invalid_redirect_uri: redirect urien som var inkludert er ikke gyldig.
+        invalid_request: Forespørslen mangler ett eller flere parametere, inkluderte ett parameter som ikke støttes eller har feil struktur.
+        invalid_resource_owner: Ressurseierens detaljer er ikke gyldig, eller så kan ikke eieren finnes.
+        invalid_scope: Det etterspurte omfanget er ugyldig, ukjent eller har feil struktur.
+        invalid_token:
+          expired: Tilgangsbeviset har utløpt
+          revoked: Tilgangsbeviset har blitt opphevet
+          unknown: Tilgangsbeviset er ugyldig
+        resource_owner_authenticator_not_configured: Ressurseier kunne ikke finnes fordi Doorkeeper.configure.resource_owner_authenticator ikke er konfigurert.
+        server_error: Autoriserings tjeneren støtte på en uventet hendelse som hindret den i å svare på forespørslen.
+        temporarily_unavailable: Autoriserings tjeneren kan ikke håndtere forespørslen grunnet en midlertidig overbelastning eller tjenervedlikehold.
+        unauthorized_client: Klienten har ikke autorisasjon for å utføre denne forespørslen med denne metoden.
+        unsupported_grant_type: Autorisasjons tildelings typen er ikke støttet av denne autoriserings tjeneren.
+        unsupported_response_type: Autorisasjons serveren støtter ikke denne typen av forespørsler.
+    flash:
+      applications:
+        create:
+          notice: Applikasjon opprettet.
+        destroy:
+          notice: Applikasjon slettet.
+        update:
+          notice: Applikasjon oppdatert.
+      authorized_applications:
+        destroy:
+          notice: Applikasjon opphevet.
+    layouts:
+      admin:
+        nav:
+          applications: Applikasjoner
+          oauth2_provider: OAuth2 tilbyder
+      application:
+        title: OAuth autorisering påkrevet
+    scopes:
+      follow: følg, blokker, avblokker, avfølg kontoer
+      read: lese dine data
+      write: poste på dine vegne
diff --git a/config/locales/no.yml b/config/locales/no.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b9a752d5af818fdb4b02be21dd141f20d24fdece
--- /dev/null
+++ b/config/locales/no.yml
@@ -0,0 +1,164 @@
+---
+'no':
+  about:
+    about_mastodon: Mastodon er et <em>gratis, åpen kildekode</em> sosialt nettverk. Et <em>desentralisert</em> alternativ til kommersielle plattformer. Slik kan det unngå risikoene ved å ha et enkelt selskap med monopol på din kommunikasjon. Velg en tjener du stoler på &mdash; uansett hvilken du velger så kan du interagere med alle andre. Alle kan kjøre sin egen Mastodon og delta sømløst i det sosiale nettverket.
+    about_this: Om denne instansen
+    apps: Applikasjoner
+    business_email: 'Bedriftsepost:'
+    contact: Kontakt
+    description_headline: Hva er %{domain}?
+    domain_count_after: andre instanser
+    domain_count_before: Koblet til
+    features:
+      api: Åpent api for applikasjoner og tjenester
+      blocks: Rikholdige blokkerings verktøy
+      characters: 500 tegn per post
+      chronology: Tidslinjer er kronologiske
+      ethics: 'Etisk design: Ingen reklame, ingen sporing'
+      gifv: GIFV sett og korte videoer
+      privacy: Finmaskete personvernsinnstillinger
+      public: Offentlige tidslinjer
+    features_headline: Hva skiller Mastodon fra andre sosiale nettverk
+    get_started: Kom i gang
+    links: Lenker
+    other_instances: Andre instanser
+    source_code: Kildekode
+    status_count_after: statuser
+    status_count_before: Hvem skrev
+    terms: Betingelser
+    user_count_after: brukere
+    user_count_before: Hjem til
+  accounts:
+    follow: Følg
+    followers: Følgere
+    following: Følger
+    nothing_here: Det er ingenting her!
+    people_followed_by: Folk som %{name} følger
+    people_who_follow: Folk som følger %{name}
+    posts: Poster
+    remote_follow: Følg fra andre instanser
+    unfollow: Avfølg
+  application_mailer:
+    settings: 'Endre foretrukne epost innstillinger: %{link}'
+    signature: Mastodon notiser fra %{instance}
+    view: 'Se:'
+  applications:
+    invalid_url: Den oppgitte URLen er ugyldig
+  auth:
+    change_password: Brukerdetaljer
+    didnt_get_confirmation: Fikk du ikke bekreftelsesmailen din?
+    forgot_password: Har du glemt passordet ditt?
+    login: Innlogging
+    logout: Logg ut
+    register: Bli med
+    resend_confirmation: Send bekreftelsesinstruksjoner på nytt
+    reset_password: Nullstill passord
+    set_new_password: Sett nytt passord
+  authorize_follow:
+    error: Uheldigvis så skjedde det en feil når vi prøvde å få tak i en konto fra en annen instans.
+    follow: Følg
+    prompt_html: 'Du (<strong>%{self}</strong>) har spurt om å følge:'
+    title: Følg %{acct}
+  datetime:
+    distance_in_words:
+      about_x_hours: "%{count}t"
+      about_x_months: "%{count}m"
+      about_x_years: "%{count}å"
+      almost_x_years: "%{count}å"
+      half_a_minute: Nylig
+      less_than_x_minutes: "%{count}min"
+      less_than_x_seconds: Nylig
+      over_x_years: "%{count}å"
+      x_days: "%{count}d"
+      x_minutes: "%{count}min"
+      x_months: "%{count}mo"
+      x_seconds: "%{count}s"
+  exports:
+    blocks: Du blokkerer
+    csv: CSV
+    follows: Du følger
+    storage: Media lagring
+  generic:
+    changes_saved_msg: Vellykket lagring av endringer!
+    powered_by: drevet av %{link}
+    save_changes: Lagre endringer
+    validation_errors:
+      one: Noe er ikke helt riktig ennå. Vær snill å se etter en gang til
+      other: Noe er ikke helt riktig ennå. Det er ennå %{count} feil å rette på
+  imports:
+    preface: Du kan importere data om mennesker du følger eller blokkerer inn til kontoen din på denne instansen, fra filer opprettet av eksporter fra andre instanser.
+    success: Din data ble mottatt og vil bli prosessert så fort som mulig.
+    types:
+      blocking: Blokkeringsliste
+      following: Følgeliste
+    upload: Opplastning
+  landing_strip_html: <strong>%{name}</strong> er en bruker på <strong>%{domain}</strong>. Du kan følge dem eller interagere med dem hvis du har en konto hvor som helst i fediverset. Hvis du ikke har en konto så kan du <a href="%{sign_up_path}">registrere deg her</a>.
+  notification_mailer:
+    digest:
+      body: 'Her er en kort oppsummering av hva du har gått glipp av på %{instance} siden du logget deg inn sist den %{since}:'
+      mention: "%{name} nevnte deg i:"
+      new_followers_summary:
+        one: Du har fått en ny følger. Jippi!
+        other: Du har fått %{count} nye følgere! Imponerende!
+      subject:
+        one: "1 ny hendelse siden ditt siste besøk \U0001F418"
+        other: "%{count} nye hendelser siden ditt siste besøk \U0001F418"
+    favourite:
+      body: 'Din status ble satt som favoritt av %{name}'
+      subject: "%{name} satte din status som favoritt."
+    follow:
+      body: "%{name} følger deg!"
+      subject: "%{name} følger deg"
+    follow_request:
+      body: "%{name} har spurt om å få lov til å følge deg"
+      subject: 'Ventende følger: %{name}'
+    mention:
+      body: 'Du ble nevnt av %{name} i:'
+      subject: Du ble nevnt av %{name}
+    reblog:
+      body: 'Din status fikk en boost av %{name}:'
+      subject: "%{name} ga din status en boost"
+  pagination:
+    next: Neste
+    prev: Forrige
+  remote_follow:
+    acct: Tast inn brukernavn@domene som du vil følge fra
+    missing_resource: Kunne ikke finne URLen for din konto
+    proceed: Fortsett med følging
+    prompt: 'Du kommer til å følge:'
+  settings:
+    authorized_apps: Autoriserte applikasjoner
+    back: Tilbake til Mastodon
+    edit_profile: Endre profil
+    export: Data eksport
+    import: Importer
+    preferences: Foretrukne valg
+    settings: Innstillinger
+    two_factor_auth: To-faktor autentisering
+  statuses:
+    open_in_web: Åpne i nettleser
+    over_character_limit: tegngrense på %{max} overskredet
+    show_more: Vis mer
+    visibilities:
+      private: Vis kun til følgere
+      public: Offentlig
+      unlisted: Offentlig, men vis ikke på offentlig tidslinje
+  stream_entries:
+    click_to_show: Klikk for å vise
+    reblogged: boostet
+    sensitive_content: Sensitivt innhold
+  time:
+    formats:
+      default: "%d, %b %Y, %H:%M"
+  two_factor_auth:
+    description_html: Hvis du skru på <strong>tofaktor autentisering</strong> vil innlogging kreve at du har telefonen din, som vil generere koder som du må taste inn.
+    disable: Skru av
+    enable: Skru på
+    instructions_html: "<strong>Scan denne QR-koden i Google Authenticator eller en lignende app på telefonen din</strong>. Fra nå av så vil denne applikasjonen generere koder for deg som skal brukes under innlogging"
+    plaintext_secret_html: 'Plain-text secret: <samp>%{secret}</samp>'
+    warning: Hvis du ikke kan konfigurere en autentikatorapp nå, så bør du trykke "Skru av"; ellers vil du ikke kunne logge inn.
+  users:
+    invalid_email: E-post addressen er ugyldig
+    invalid_otp_token: Ugyldig two-faktor kode
+  will_paginate:
+    page_gap: "&hellip;"
diff --git a/config/locales/simple_form.no.yml b/config/locales/simple_form.no.yml
new file mode 100644
index 0000000000000000000000000000000000000000..7e705b19b145d715b4cee5870e7f5a3d4dcbbff8
--- /dev/null
+++ b/config/locales/simple_form.no.yml
@@ -0,0 +1,46 @@
+---
+'no':
+  simple_form:
+    hints:
+      defaults:
+        avatar: PNG, GIF eller JPG. Maksimalt 2MB. Vil bli nedskalert til 120x120px
+        display_name: Maksimalt 30 tegn
+        header: PNG, GIF eller JPG. Maksimalt 2MB. Vil bli nedskalert til 700x335px
+        locked: Krever at du manuelt godkjenner følgere og setter standard beskyttelse av poster til kun-følgere
+        note: Maksimalt 160 tegn
+      imports:
+        data: CSV fil eksportert fra en annen Mastodon instans
+    labels:
+      defaults:
+        avatar: Avatar
+        confirm_new_password: Bekreft nytt passord
+        confirm_password: Bekreft passord
+        current_password: Nåværende passord
+        data: Data
+        display_name: Visningsnavn
+        email: E-post adresse
+        header: Header
+        locale: Språk
+        locked: Endre konto til privat
+        new_password: Nytt passord
+        note: Biografi
+        otp_attempt: To-faktor kode
+        password: Passord
+        setting_default_privacy: Leserettigheter for poster
+        type: Importeringstype
+        username: Brukernavn
+      interactions:
+        must_be_follower: Blokker meldinger fra ikke-følgere
+        must_be_following: Blokker meldinger fra folk du ikke følger
+      notification_emails:
+        digest: Send oppsummerings eposter
+        favourite: Send e-post når noen setter din status som favoritt
+        follow: Send e-post når noen følger deg
+        follow_request: Send e-post når noen spør om å få følge deg
+        mention: Send e-post når noen nevner deg
+        reblog: Send e-post når noen reblogger din status
+    'no': 'Nei'
+    required:
+      mark: "*"
+      text: påkrevd
+    'yes': 'Ja'
diff --git a/config/navigation.rb b/config/navigation.rb
index 77556e5aa38b5b2c98be1b13ac2aea5d70b9ad7d..c6b7b976789c37b7a574b4f82ba3df0f460b2a36 100644
--- a/config/navigation.rb
+++ b/config/navigation.rb
@@ -14,11 +14,11 @@ SimpleNavigation::Configuration.run do |navigation|
       settings.item :authorized_apps, safe_join([fa_icon('list fw'), t('settings.authorized_apps')]), oauth_authorized_applications_url
     end
 
-    primary.item :admin, safe_join([fa_icon('cogs fw'), 'Administration']), admin_accounts_url, if: proc { current_user.admin? } do |admin|
+    primary.item :admin, safe_join([fa_icon('cogs fw'), 'Administration']), admin_reports_url, if: proc { current_user.admin? } do |admin|
       admin.item :reports, safe_join([fa_icon('flag fw'), 'Reports']), admin_reports_url, highlights_on: %r{/admin/reports}
       admin.item :accounts, safe_join([fa_icon('users fw'), 'Accounts']), admin_accounts_url, highlights_on: %r{/admin/accounts}
       admin.item :pubsubhubbubs, safe_join([fa_icon('paper-plane-o fw'), 'PubSubHubbub']), admin_pubsubhubbub_index_url
-      admin.item :domain_blocks, safe_join([fa_icon('lock fw'), 'Domain Blocks']), admin_domain_blocks_url
+      admin.item :domain_blocks, safe_join([fa_icon('lock fw'), 'Domain Blocks']), admin_domain_blocks_url, highlights_on: %r{/admin/domain_blocks}
       admin.item :sidekiq, safe_join([fa_icon('diamond fw'), 'Sidekiq']), sidekiq_url
       admin.item :pghero, safe_join([fa_icon('database fw'), 'PgHero']), pghero_url
       admin.item :settings, safe_join([fa_icon('cogs fw'), 'Site Settings']), admin_settings_url
diff --git a/config/routes.rb b/config/routes.rb
index bfca5c734b9fda920fc3377e140407c5a2372da6..ca77191f7c8fa7655d227935d0f15d6b8a41f8ba 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -77,7 +77,7 @@ Rails.application.routes.draw do
 
   namespace :admin do
     resources :pubsubhubbub, only: [:index]
-    resources :domain_blocks, only: [:index, :create]
+    resources :domain_blocks, only: [:index, :new, :create]
     resources :settings, only: [:index, :update]
 
     resources :reports, only: [:index, :show] do
diff --git a/db/migrate/20170403172249_add_action_taken_by_account_id_to_reports.rb b/db/migrate/20170403172249_add_action_taken_by_account_id_to_reports.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2d4e1219802e7171a1d7a26a1684117a7810b940
--- /dev/null
+++ b/db/migrate/20170403172249_add_action_taken_by_account_id_to_reports.rb
@@ -0,0 +1,5 @@
+class AddActionTakenByAccountIdToReports < ActiveRecord::Migration[5.0]
+  def change
+    add_column :reports, :action_taken_by_account_id, :integer
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 5a9ca1426d94c119a20550c5ade297cac19ff4a1..3aaa3e3ad7b5d6cd7cdb3b0fb03cb8654bf456b9 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 20170330164118) do
+ActiveRecord::Schema.define(version: 20170403172249) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -201,13 +201,14 @@ ActiveRecord::Schema.define(version: 20170330164118) do
   end
 
   create_table "reports", force: :cascade do |t|
-    t.integer  "account_id",                        null: false
-    t.integer  "target_account_id",                 null: false
-    t.bigint   "status_ids",        default: [],    null: false, array: true
-    t.text     "comment",           default: "",    null: false
-    t.boolean  "action_taken",      default: false, null: false
-    t.datetime "created_at",                        null: false
-    t.datetime "updated_at",                        null: false
+    t.integer  "account_id",                                 null: false
+    t.integer  "target_account_id",                          null: false
+    t.bigint   "status_ids",                 default: [],    null: false, array: true
+    t.text     "comment",                    default: "",    null: false
+    t.boolean  "action_taken",               default: false, null: false
+    t.datetime "created_at",                                 null: false
+    t.datetime "updated_at",                                 null: false
+    t.integer  "action_taken_by_account_id"
   end
 
   create_table "settings", force: :cascade do |t|
diff --git a/docker-compose.yml b/docker-compose.yml
index 68c8ef960e34d288e03401230a2404b1cf326ae9..d6ba66ddeb49927cb55e1fb878e1499db716fdb8 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -33,7 +33,7 @@ services:
     restart: always
     build: .
     env_file: .env.production
-    command: bundle exec sidekiq -q default -q mailers -q push
+    command: bundle exec sidekiq -q default -q mailers -q pull -q push
     depends_on:
       - db
       - redis
diff --git a/docs/Running-Mastodon/Production-guide.md b/docs/Running-Mastodon/Production-guide.md
index f0dd7bd2b595387b9cc27151a3d5671117bb8137..469fefa94355568d3dc0268deebeb50c7de09034 100644
--- a/docs/Running-Mastodon/Production-guide.md
+++ b/docs/Running-Mastodon/Production-guide.md
@@ -180,7 +180,7 @@ User=mastodon
 WorkingDirectory=/home/mastodon/live
 Environment="RAILS_ENV=production"
 Environment="DB_POOL=5"
-ExecStart=/home/mastodon/.rbenv/shims/bundle exec sidekiq -c 5 -q default -q mailers -q push
+ExecStart=/home/mastodon/.rbenv/shims/bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push
 TimeoutSec=15
 Restart=always
 
diff --git a/docs/Using-Mastodon/List-of-Mastodon-instances.md b/docs/Using-Mastodon/List-of-Mastodon-instances.md
index 7c8a33893cb86ae76e64ba2c221aee11030dfc22..780977bd471ac44c73c22fff7bceaf618b201f50 100644
--- a/docs/Using-Mastodon/List-of-Mastodon-instances.md
+++ b/docs/Using-Mastodon/List-of-Mastodon-instances.md
@@ -21,5 +21,6 @@ There is also a list at [instances.mastodon.xyz](https://instances.mastodon.xyz)
 | [social.mashek.net](https://social.mashek.net) |Themed and customised for Mashekstein Labs community. Selectively federates.|Yes|No|
 | [masto.themimitoof.fr](https://masto.themimitoof.fr) |N/A|Yes|Yes|
 | [social.imirhil.fr](https://social.imirhil.fr) |N/A|No|Yes|
+| [social.wxcafe.net](https://social.wxcafe.net) |Open registrations, federates everywhere, no moderation yet|Yes|Yes|
 
 Let me know if you start running one so I can add it to the list! (Alternatively, add it yourself as a pull request).
diff --git a/spec/services/block_domain_service_spec.rb b/spec/services/block_domain_service_spec.rb
index d88b3b55cf7b5f89ba4875f21b208cf77d78fbe4..8e71d4542922f6e878cc77396e8d0a99e651ffb0 100644
--- a/spec/services/block_domain_service_spec.rb
+++ b/spec/services/block_domain_service_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe BlockDomainService do
     bad_status2
     bad_attachment
 
-    subject.call('evil.org', :suspend)
+    subject.call(DomainBlock.create!(domain: 'evil.org', severity: :suspend))
   end
 
   it 'creates a domain block' do