diff --git a/app/controllers/api/proofs_controller.rb b/app/controllers/api/proofs_controller.rb
deleted file mode 100644
index dd32cd577bfceaaba93f6387274f9d957be39818..0000000000000000000000000000000000000000
--- a/app/controllers/api/proofs_controller.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-class Api::ProofsController < Api::BaseController
-  include AccountOwnedConcern
-
-  skip_before_action :require_authenticated_user!
-
-  before_action :set_provider
-
-  def index
-    render json: @account, serializer: @provider.serializer_class
-  end
-
-  private
-
-  def set_provider
-    @provider = ProofProvider.find(params[:provider]) || raise(ActiveRecord::RecordNotFound)
-  end
-
-  def username_param
-    params[:username]
-  end
-end
diff --git a/app/controllers/api/v1/accounts/identity_proofs_controller.rb b/app/controllers/api/v1/accounts/identity_proofs_controller.rb
index 4b5f6902c7df95345cbbf3677d977f639b040813..48f293f47a325be7d9e5f40fd45aa9c8b01d3f29 100644
--- a/app/controllers/api/v1/accounts/identity_proofs_controller.rb
+++ b/app/controllers/api/v1/accounts/identity_proofs_controller.rb
@@ -5,8 +5,7 @@ class Api::V1::Accounts::IdentityProofsController < Api::BaseController
   before_action :set_account
 
   def index
-    @proofs = @account.suspended? ? [] : @account.identity_proofs.active
-    render json: @proofs, each_serializer: REST::IdentityProofSerializer
+    render json: []
   end
 
   private
diff --git a/app/controllers/settings/identity_proofs_controller.rb b/app/controllers/settings/identity_proofs_controller.rb
deleted file mode 100644
index bf2899da66fc4424b0d72abbd7617f4d78822371..0000000000000000000000000000000000000000
--- a/app/controllers/settings/identity_proofs_controller.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-# frozen_string_literal: true
-
-class Settings::IdentityProofsController < Settings::BaseController
-  before_action :check_required_params, only: :new
-
-  def index
-    @proofs = AccountIdentityProof.where(account: current_account).order(provider: :asc, provider_username: :asc)
-    @proofs.each(&:refresh!)
-  end
-
-  def new
-    @proof = current_account.identity_proofs.new(
-      token: params[:token],
-      provider: params[:provider],
-      provider_username: params[:provider_username]
-    )
-
-    if current_account.username.casecmp(params[:username]).zero?
-      render layout: 'auth'
-    else
-      redirect_to settings_identity_proofs_path, alert: I18n.t('identity_proofs.errors.wrong_user', proving: params[:username], current: current_account.username)
-    end
-  end
-
-  def create
-    @proof = current_account.identity_proofs.where(provider: resource_params[:provider], provider_username: resource_params[:provider_username]).first_or_initialize(resource_params)
-    @proof.token = resource_params[:token]
-
-    if @proof.save
-      PostStatusService.new.call(current_user.account, text: post_params[:status_text]) if publish_proof?
-      redirect_to @proof.on_success_path(params[:user_agent])
-    else
-      redirect_to settings_identity_proofs_path, alert: I18n.t('identity_proofs.errors.failed', provider: @proof.provider.capitalize)
-    end
-  end
-
-  def destroy
-    @proof = current_account.identity_proofs.find(params[:id])
-    @proof.destroy!
-    redirect_to settings_identity_proofs_path, success: I18n.t('identity_proofs.removed')
-  end
-
-  private
-
-  def check_required_params
-    redirect_to settings_identity_proofs_path unless [:provider, :provider_username, :username, :token].all? { |k| params[k].present? }
-  end
-
-  def resource_params
-    params.require(:account_identity_proof).permit(:provider, :provider_username, :token)
-  end
-
-  def publish_proof?
-    ActiveModel::Type::Boolean.new.cast(post_params[:post_status])
-  end
-
-  def post_params
-    params.require(:account_identity_proof).permit(:post_status, :status_text)
-  end
-end
diff --git a/app/controllers/well_known/keybase_proof_config_controller.rb b/app/controllers/well_known/keybase_proof_config_controller.rb
deleted file mode 100644
index e1d43ecbe9a1ad9c18c5944abbd8b81a2596bb05..0000000000000000000000000000000000000000
--- a/app/controllers/well_known/keybase_proof_config_controller.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-module WellKnown
-  class KeybaseProofConfigController < ActionController::Base
-    def show
-      render json: {}, serializer: ProofProvider::Keybase::ConfigSerializer, root: 'keybase_config'
-    end
-  end
-end
diff --git a/app/javascript/mastodon/actions/identity_proofs.js b/app/javascript/mastodon/actions/identity_proofs.js
deleted file mode 100644
index 10398395660345ac202f2a76a0487d6001ac54a0..0000000000000000000000000000000000000000
--- a/app/javascript/mastodon/actions/identity_proofs.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import api from '../api';
-
-export const IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST = 'IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST';
-export const IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS = 'IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS';
-export const IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL    = 'IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL';
-
-export const fetchAccountIdentityProofs = accountId => (dispatch, getState) => {
-  dispatch(fetchAccountIdentityProofsRequest(accountId));
-
-  api(getState).get(`/api/v1/accounts/${accountId}/identity_proofs`)
-    .then(({ data }) => dispatch(fetchAccountIdentityProofsSuccess(accountId, data)))
-    .catch(err => dispatch(fetchAccountIdentityProofsFail(accountId, err)));
-};
-
-export const fetchAccountIdentityProofsRequest = id => ({
-  type: IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST,
-  id,
-});
-
-export const fetchAccountIdentityProofsSuccess = (accountId, identity_proofs) => ({
-  type: IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS,
-  accountId,
-  identity_proofs,
-});
-
-export const fetchAccountIdentityProofsFail = (accountId, err) => ({
-  type: IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL,
-  accountId,
-  err,
-  skipNotFound: true,
-});
diff --git a/app/javascript/mastodon/features/account/components/header.js b/app/javascript/mastodon/features/account/components/header.js
index 4d0a828c70ddcdae92db20de335232630336509b..48ec49d8148ee1a8bb07f61790dbebc2ff74913a 100644
--- a/app/javascript/mastodon/features/account/components/header.js
+++ b/app/javascript/mastodon/features/account/components/header.js
@@ -123,7 +123,7 @@ class Header extends ImmutablePureComponent {
   }
 
   render () {
-    const { account, intl, domain, identity_proofs } = this.props;
+    const { account, intl, domain } = this.props;
 
     if (!account) {
       return null;
@@ -297,20 +297,8 @@ class Header extends ImmutablePureComponent {
 
           <div className='account__header__extra'>
             <div className='account__header__bio'>
-              {(fields.size > 0 || identity_proofs.size > 0) && (
+              {fields.size > 0 && (
                 <div className='account__header__fields'>
-                  {identity_proofs.map((proof, i) => (
-                    <dl key={i}>
-                      <dt dangerouslySetInnerHTML={{ __html: proof.get('provider') }} />
-
-                      <dd className='verified'>
-                        <a href={proof.get('proof_url')} target='_blank' rel='noopener noreferrer'><span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(proof.get('updated_at'), dateFormatOptions) })}>
-                          <Icon id='check' className='verified__mark' />
-                        </span></a>
-                        <a href={proof.get('profile_url')} target='_blank' rel='noopener noreferrer'><span dangerouslySetInnerHTML={{ __html: ' '+proof.get('provider_username') }} /></a>
-                      </dd>
-                    </dl>
-                  ))}
                   {fields.map((pair, i) => (
                     <dl key={i}>
                       <dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} className='translate' />
diff --git a/app/javascript/mastodon/features/account_timeline/components/header.js b/app/javascript/mastodon/features/account_timeline/components/header.js
index 17b693600e5c3f9c16a9aa6e895d7dcb2fd7aad8..33bea4c1799b9d800901ce401233e2247265805d 100644
--- a/app/javascript/mastodon/features/account_timeline/components/header.js
+++ b/app/javascript/mastodon/features/account_timeline/components/header.js
@@ -11,7 +11,6 @@ export default class Header extends ImmutablePureComponent {
 
   static propTypes = {
     account: ImmutablePropTypes.map,
-    identity_proofs: ImmutablePropTypes.list,
     onFollow: PropTypes.func.isRequired,
     onBlock: PropTypes.func.isRequired,
     onMention: PropTypes.func.isRequired,
@@ -92,7 +91,7 @@ export default class Header extends ImmutablePureComponent {
   }
 
   render () {
-    const { account, hideTabs, identity_proofs } = this.props;
+    const { account, hideTabs } = this.props;
 
     if (account === null) {
       return null;
@@ -104,7 +103,6 @@ export default class Header extends ImmutablePureComponent {
 
         <InnerHeader
           account={account}
-          identity_proofs={identity_proofs}
           onFollow={this.handleFollow}
           onBlock={this.handleBlock}
           onMention={this.handleMention}
diff --git a/app/javascript/mastodon/features/account_timeline/containers/header_container.js b/app/javascript/mastodon/features/account_timeline/containers/header_container.js
index e12019547ec5ed0a3e2daf09ed4a9a3789e8f427..b3f8521cb95665560d3b249616d4b04e53bd3a16 100644
--- a/app/javascript/mastodon/features/account_timeline/containers/header_container.js
+++ b/app/javascript/mastodon/features/account_timeline/containers/header_container.js
@@ -21,7 +21,6 @@ import { openModal } from '../../../actions/modal';
 import { blockDomain, unblockDomain } from '../../../actions/domain_blocks';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import { unfollowModal } from '../../../initial_state';
-import { List as ImmutableList } from 'immutable';
 
 const messages = defineMessages({
   unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' },
@@ -34,7 +33,6 @@ const makeMapStateToProps = () => {
   const mapStateToProps = (state, { accountId }) => ({
     account: getAccount(state, accountId),
     domain: state.getIn(['meta', 'domain']),
-    identity_proofs: state.getIn(['identity_proofs', accountId], ImmutableList()),
   });
 
   return mapStateToProps;
diff --git a/app/javascript/mastodon/features/account_timeline/index.js b/app/javascript/mastodon/features/account_timeline/index.js
index 20f1dba9f5bf5c6091c8fdd7a8b7ccb1174dddbc..37df2818b199efa1ab48cb1dccd3d4d5d691d577 100644
--- a/app/javascript/mastodon/features/account_timeline/index.js
+++ b/app/javascript/mastodon/features/account_timeline/index.js
@@ -12,7 +12,6 @@ import ColumnBackButton from '../../components/column_back_button';
 import { List as ImmutableList } from 'immutable';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import { FormattedMessage } from 'react-intl';
-import { fetchAccountIdentityProofs } from '../../actions/identity_proofs';
 import MissingIndicator from 'mastodon/components/missing_indicator';
 import TimelineHint from 'mastodon/components/timeline_hint';
 import { me } from 'mastodon/initial_state';
@@ -80,7 +79,6 @@ class AccountTimeline extends ImmutablePureComponent {
     const { accountId, withReplies, dispatch } = this.props;
 
     dispatch(fetchAccount(accountId));
-    dispatch(fetchAccountIdentityProofs(accountId));
 
     if (!withReplies) {
       dispatch(expandAccountFeaturedTimeline(accountId));
diff --git a/app/javascript/mastodon/reducers/identity_proofs.js b/app/javascript/mastodon/reducers/identity_proofs.js
deleted file mode 100644
index 58af0a5faaa780b64506fa8e2a569d1a2e8c8ea7..0000000000000000000000000000000000000000
--- a/app/javascript/mastodon/reducers/identity_proofs.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import { Map as ImmutableMap, fromJS } from 'immutable';
-import {
-  IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST,
-  IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS,
-  IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL,
-} from '../actions/identity_proofs';
-
-const initialState = ImmutableMap();
-
-export default function identityProofsReducer(state = initialState, action) {
-  switch(action.type) {
-  case IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST:
-    return state.set('isLoading', true);
-  case IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL:
-    return state.set('isLoading', false);
-  case IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS:
-    return state.update(identity_proofs => identity_proofs.withMutations(map => {
-      map.set('isLoading', false);
-      map.set('loaded', true);
-      map.set(action.accountId, fromJS(action.identity_proofs));
-    }));
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/mastodon/reducers/index.js b/app/javascript/mastodon/reducers/index.js
index e518c8228ab3637ea8413671448962a43cbd2db4..53e2dd681ee5f1ded7d758700a0ced946074c391 100644
--- a/app/javascript/mastodon/reducers/index.js
+++ b/app/javascript/mastodon/reducers/index.js
@@ -32,7 +32,6 @@ import filters from './filters';
 import conversations from './conversations';
 import suggestions from './suggestions';
 import polls from './polls';
-import identity_proofs from './identity_proofs';
 import trends from './trends';
 import missed_updates from './missed_updates';
 import announcements from './announcements';
@@ -69,7 +68,6 @@ const reducers = {
   notifications,
   height_cache,
   custom_emojis,
-  identity_proofs,
   lists,
   listEditor,
   listAdder,
diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss
index 5b71b6334eee2476d8c0bc5347203597b11bc92c..65f53471d7729bcc4b91a5796c18d13ae80c6058 100644
--- a/app/javascript/styles/mastodon/forms.scss
+++ b/app/javascript/styles/mastodon/forms.scss
@@ -999,68 +999,6 @@ code {
   }
 }
 
-.connection-prompt {
-  margin-bottom: 25px;
-
-  .fa-link {
-    background-color: darken($ui-base-color, 4%);
-    border-radius: 100%;
-    font-size: 24px;
-    padding: 10px;
-  }
-
-  &__column {
-    align-items: center;
-    display: flex;
-    flex: 1;
-    flex-direction: column;
-    flex-shrink: 1;
-    max-width: 50%;
-
-    &-sep {
-      align-self: center;
-      flex-grow: 0;
-      overflow: visible;
-      position: relative;
-      z-index: 1;
-    }
-
-    p {
-      word-break: break-word;
-    }
-  }
-
-  .account__avatar {
-    margin-bottom: 20px;
-  }
-
-  &__connection {
-    background-color: lighten($ui-base-color, 8%);
-    box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
-    border-radius: 4px;
-    padding: 25px 10px;
-    position: relative;
-    text-align: center;
-
-    &::after {
-      background-color: darken($ui-base-color, 4%);
-      content: '';
-      display: block;
-      height: 100%;
-      left: 50%;
-      position: absolute;
-      top: 0;
-      width: 1px;
-    }
-  }
-
-  &__row {
-    align-items: flex-start;
-    display: flex;
-    flex-direction: row;
-  }
-}
-
 .input.user_confirm_password,
 .input.user_website {
   &:not(.field_with_errors) {
diff --git a/app/lib/activitypub/adapter.rb b/app/lib/activitypub/adapter.rb
index 2d6b876593752eb48a2af6f29dd707aa1928db03..776e1d3daecc6d6ccc62c0f4cbd872535f4985ce 100644
--- a/app/lib/activitypub/adapter.rb
+++ b/app/lib/activitypub/adapter.rb
@@ -18,7 +18,6 @@ class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base
     atom_uri: { 'ostatus' => 'http://ostatus.org#', 'atomUri' => 'ostatus:atomUri' },
     conversation: { 'ostatus' => 'http://ostatus.org#', 'inReplyToAtomUri' => 'ostatus:inReplyToAtomUri', 'conversation' => 'ostatus:conversation' },
     focal_point: { 'toot' => 'http://joinmastodon.org/ns#', 'focalPoint' => { '@container' => '@list', '@id' => 'toot:focalPoint' } },
-    identity_proof: { 'toot' => 'http://joinmastodon.org/ns#', 'IdentityProof' => 'toot:IdentityProof' },
     blurhash: { 'toot' => 'http://joinmastodon.org/ns#', 'blurhash' => 'toot:blurhash' },
     discoverable: { 'toot' => 'http://joinmastodon.org/ns#', 'discoverable' => 'toot:discoverable' },
     voters_count: { 'toot' => 'http://joinmastodon.org/ns#', 'votersCount' => 'toot:votersCount' },
diff --git a/app/lib/proof_provider.rb b/app/lib/proof_provider.rb
deleted file mode 100644
index 102c50f4f9ef83e3abf8773bd4f81ea2b54a24f2..0000000000000000000000000000000000000000
--- a/app/lib/proof_provider.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-# frozen_string_literal: true
-
-module ProofProvider
-  SUPPORTED_PROVIDERS = %w(keybase).freeze
-
-  def self.find(identifier, proof = nil)
-    case identifier
-    when 'keybase'
-      ProofProvider::Keybase.new(proof)
-    end
-  end
-end
diff --git a/app/lib/proof_provider/keybase.rb b/app/lib/proof_provider/keybase.rb
deleted file mode 100644
index 8e51d714646497bbb5e08f0e892fdcf51a852715..0000000000000000000000000000000000000000
--- a/app/lib/proof_provider/keybase.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-# frozen_string_literal: true
-
-class ProofProvider::Keybase
-  BASE_URL = ENV.fetch('KEYBASE_BASE_URL', 'https://keybase.io')
-  DOMAIN   = ENV.fetch('KEYBASE_DOMAIN', Rails.configuration.x.web_domain)
-
-  class Error < StandardError; end
-
-  class ExpectedProofLiveError < Error; end
-
-  class UnexpectedResponseError < Error; end
-
-  def initialize(proof = nil)
-    @proof = proof
-  end
-
-  def serializer_class
-    ProofProvider::Keybase::Serializer
-  end
-
-  def worker_class
-    ProofProvider::Keybase::Worker
-  end
-
-  def validate!
-    unless @proof.token&.size == 66
-      @proof.errors.add(:base, I18n.t('identity_proofs.errors.keybase.invalid_token'))
-      return
-    end
-
-    # Do not perform synchronous validation for remote accounts
-    return if @proof.provider_username.blank? || !@proof.account.local?
-
-    if verifier.valid?
-      @proof.verified = true
-      @proof.live     = false
-    else
-      @proof.errors.add(:base, I18n.t('identity_proofs.errors.keybase.verification_failed', kb_username: @proof.provider_username))
-    end
-  end
-
-  def refresh!
-    worker_class.new.perform(@proof)
-  rescue ProofProvider::Keybase::Error
-    nil
-  end
-
-  def on_success_path(user_agent = nil)
-    verifier.on_success_path(user_agent)
-  end
-
-  def badge
-    @badge ||= ProofProvider::Keybase::Badge.new(@proof.account.username, @proof.provider_username, @proof.token, domain)
-  end
-
-  def verifier
-    @verifier ||= ProofProvider::Keybase::Verifier.new(@proof.account.username, @proof.provider_username, @proof.token, domain)
-  end
-
-  private
-
-  def domain
-    if @proof.account.local?
-      DOMAIN
-    else
-      @proof.account.domain
-    end
-  end
-end
diff --git a/app/lib/proof_provider/keybase/badge.rb b/app/lib/proof_provider/keybase/badge.rb
deleted file mode 100644
index f587b1cc74480f152d8c0b75b6707a551036fc3b..0000000000000000000000000000000000000000
--- a/app/lib/proof_provider/keybase/badge.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-# frozen_string_literal: true
-
-class ProofProvider::Keybase::Badge
-  include RoutingHelper
-
-  def initialize(local_username, provider_username, token, domain)
-    @local_username    = local_username
-    @provider_username = provider_username
-    @token             = token
-    @domain            = domain
-  end
-
-  def proof_url
-    "#{ProofProvider::Keybase::BASE_URL}/#{@provider_username}/sigchain\##{@token}"
-  end
-
-  def profile_url
-    "#{ProofProvider::Keybase::BASE_URL}/#{@provider_username}"
-  end
-
-  def icon_url
-    "#{ProofProvider::Keybase::BASE_URL}/#{@provider_username}/proof_badge/#{@token}?username=#{@local_username}&domain=#{@domain}"
-  end
-
-  def avatar_url
-    Rails.cache.fetch("proof_providers/keybase/#{@provider_username}/avatar_url", expires_in: 5.minutes) { remote_avatar_url } || default_avatar_url
-  end
-
-  private
-
-  def remote_avatar_url
-    request = Request.new(:get, "#{ProofProvider::Keybase::BASE_URL}/_/api/1.0/user/pic_url.json", params: { username: @provider_username })
-
-    request.perform do |res|
-      json = Oj.load(res.body_with_limit, mode: :strict)
-      json['pic_url'] if json.is_a?(Hash)
-    end
-  rescue Oj::ParseError, HTTP::Error, OpenSSL::SSL::SSLError
-    nil
-  end
-
-  def default_avatar_url
-    asset_pack_path('media/images/proof_providers/keybase.png')
-  end
-end
diff --git a/app/lib/proof_provider/keybase/config_serializer.rb b/app/lib/proof_provider/keybase/config_serializer.rb
deleted file mode 100644
index c6c364d316f38f46d7f749219f22bfb1600c803c..0000000000000000000000000000000000000000
--- a/app/lib/proof_provider/keybase/config_serializer.rb
+++ /dev/null
@@ -1,76 +0,0 @@
-# frozen_string_literal: true
-
-class ProofProvider::Keybase::ConfigSerializer < ActiveModel::Serializer
-  include RoutingHelper
-  include ActionView::Helpers::TextHelper
-
-  attributes :version, :domain, :display_name, :username,
-             :brand_color, :logo, :description, :prefill_url,
-             :profile_url, :check_url, :check_path, :avatar_path,
-             :contact
-
-  def version
-    1
-  end
-
-  def domain
-    ProofProvider::Keybase::DOMAIN
-  end
-
-  def display_name
-    Setting.site_title
-  end
-
-  def logo
-    {
-      svg_black: full_asset_url(asset_pack_path('media/images/logo_transparent_black.svg')),
-      svg_white: full_asset_url(asset_pack_path('media/images/logo_transparent_white.svg')),
-      svg_full: full_asset_url(asset_pack_path('media/images/logo.svg')),
-      svg_full_darkmode: full_asset_url(asset_pack_path('media/images/logo.svg')),
-    }
-  end
-
-  def brand_color
-    '#282c37'
-  end
-
-  def description
-    strip_tags(Setting.site_short_description.presence || I18n.t('about.about_mastodon_html'))
-  end
-
-  def username
-    { min: 1, max: 30, re: '[a-z0-9_]+([a-z0-9_.-]+[a-z0-9_]+)?' }
-  end
-
-  def prefill_url
-    params = {
-      provider: 'keybase',
-      token: '%{sig_hash}',
-      provider_username: '%{kb_username}',
-      username: '%{username}',
-      user_agent: '%{kb_ua}',
-    }
-
-    CGI.unescape(new_settings_identity_proof_url(params))
-  end
-
-  def profile_url
-    CGI.unescape(short_account_url('%{username}'))
-  end
-
-  def check_url
-    CGI.unescape(api_proofs_url(username: '%{username}', provider: 'keybase'))
-  end
-
-  def check_path
-    ['signatures']
-  end
-
-  def avatar_path
-    ['avatar']
-  end
-
-  def contact
-    [Setting.site_contact_email.presence || 'unknown'].compact
-  end
-end
diff --git a/app/lib/proof_provider/keybase/serializer.rb b/app/lib/proof_provider/keybase/serializer.rb
deleted file mode 100644
index d29283600ef35bf2f29501a3f23b7963f1589590..0000000000000000000000000000000000000000
--- a/app/lib/proof_provider/keybase/serializer.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-# frozen_string_literal: true
-
-class ProofProvider::Keybase::Serializer < ActiveModel::Serializer
-  include RoutingHelper
-
-  attribute :avatar
-
-  has_many :identity_proofs, key: :signatures
-
-  def avatar
-    full_asset_url(object.avatar_original_url)
-  end
-
-  class AccountIdentityProofSerializer < ActiveModel::Serializer
-    attributes :sig_hash, :kb_username
-
-    def sig_hash
-      object.token
-    end
-
-    def kb_username
-      object.provider_username
-    end
-  end
-end
diff --git a/app/lib/proof_provider/keybase/verifier.rb b/app/lib/proof_provider/keybase/verifier.rb
deleted file mode 100644
index af69b1bfc84676b3fcd69c4b10ca20fa1e1ee114..0000000000000000000000000000000000000000
--- a/app/lib/proof_provider/keybase/verifier.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-# frozen_string_literal: true
-
-class ProofProvider::Keybase::Verifier
-  def initialize(local_username, provider_username, token, domain)
-    @local_username    = local_username
-    @provider_username = provider_username
-    @token             = token
-    @domain            = domain
-  end
-
-  def valid?
-    request = Request.new(:get, "#{ProofProvider::Keybase::BASE_URL}/_/api/1.0/sig/proof_valid.json", params: query_params)
-
-    request.perform do |res|
-      json = Oj.load(res.body_with_limit, mode: :strict)
-
-      if json.is_a?(Hash)
-        json.fetch('proof_valid', false)
-      else
-        false
-      end
-    end
-  rescue Oj::ParseError, HTTP::Error, OpenSSL::SSL::SSLError
-    false
-  end
-
-  def on_success_path(user_agent = nil)
-    url = Addressable::URI.parse("#{ProofProvider::Keybase::BASE_URL}/_/proof_creation_success")
-    url.query_values = query_params.merge(kb_ua: user_agent || 'unknown')
-    url.to_s
-  end
-
-  def status
-    request = Request.new(:get, "#{ProofProvider::Keybase::BASE_URL}/_/api/1.0/sig/proof_live.json", params: query_params)
-
-    request.perform do |res|
-      raise ProofProvider::Keybase::UnexpectedResponseError unless res.code == 200
-
-      json = Oj.load(res.body_with_limit, mode: :strict)
-
-      raise ProofProvider::Keybase::UnexpectedResponseError unless json.is_a?(Hash) && json.key?('proof_valid') && json.key?('proof_live')
-
-      json
-    end
-  rescue Oj::ParseError, HTTP::Error, OpenSSL::SSL::SSLError
-    raise ProofProvider::Keybase::UnexpectedResponseError
-  end
-
-  private
-
-  def query_params
-    {
-      domain: @domain,
-      kb_username: @provider_username,
-      username: @local_username,
-      sig_hash: @token,
-    }
-  end
-end
diff --git a/app/lib/proof_provider/keybase/worker.rb b/app/lib/proof_provider/keybase/worker.rb
deleted file mode 100644
index bcdd18cc56145d524c7b264cd067fe8bc72f656f..0000000000000000000000000000000000000000
--- a/app/lib/proof_provider/keybase/worker.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# frozen_string_literal: true
-
-class ProofProvider::Keybase::Worker
-  include Sidekiq::Worker
-
-  sidekiq_options queue: 'pull', retry: 20, unique: :until_executed
-
-  sidekiq_retry_in do |count, exception|
-    # Retry aggressively when the proof is valid but not live in Keybase.
-    # This is likely because Keybase just hasn't noticed the proof being
-    # served from here yet.
-
-    if exception.class == ProofProvider::Keybase::ExpectedProofLiveError
-      case count
-      when 0..2 then 0.seconds
-      when 2..6 then 1.second
-      end
-    end
-  end
-
-  def perform(proof_id)
-    proof  = proof_id.is_a?(AccountIdentityProof) ? proof_id : AccountIdentityProof.find(proof_id)
-    status = proof.provider_instance.verifier.status
-
-    # If Keybase thinks the proof is valid, and it exists here in Mastodon,
-    # then it should be live. Keybase just has to notice that it's here
-    # and then update its state. That might take a couple seconds.
-    raise ProofProvider::Keybase::ExpectedProofLiveError if status['proof_valid'] && !status['proof_live']
-
-    proof.update!(verified: status['proof_valid'], live: status['proof_live'])
-  end
-end
diff --git a/app/models/account_identity_proof.rb b/app/models/account_identity_proof.rb
deleted file mode 100644
index 10b66cccf9b55b581013004ccd9bfe5f196e1ab2..0000000000000000000000000000000000000000
--- a/app/models/account_identity_proof.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-# == Schema Information
-#
-# Table name: account_identity_proofs
-#
-#  id                :bigint(8)        not null, primary key
-#  account_id        :bigint(8)
-#  provider          :string           default(""), not null
-#  provider_username :string           default(""), not null
-#  token             :text             default(""), not null
-#  verified          :boolean          default(FALSE), not null
-#  live              :boolean          default(FALSE), not null
-#  created_at        :datetime         not null
-#  updated_at        :datetime         not null
-#
-
-class AccountIdentityProof < ApplicationRecord
-  belongs_to :account
-
-  validates :provider, inclusion: { in: ProofProvider::SUPPORTED_PROVIDERS }
-  validates :provider_username, format: { with: /\A[a-z0-9_]+\z/i }, length: { minimum: 2, maximum: 30 }
-  validates :provider_username, uniqueness: { scope: [:account_id, :provider] }
-  validates :token, format: { with: /\A[a-f0-9]+\z/ }, length: { maximum: 66 }
-
-  validate :validate_with_provider, if: :token_changed?
-
-  scope :active, -> { where(verified: true, live: true) }
-
-  after_commit :queue_worker, if: :saved_change_to_token?
-
-  delegate :refresh!, :on_success_path, :badge, to: :provider_instance
-
-  def provider_instance
-    @provider_instance ||= ProofProvider.find(provider, self)
-  end
-
-  private
-
-  def queue_worker
-    provider_instance.worker_class.perform_async(id)
-  end
-
-  def validate_with_provider
-    provider_instance.validate!
-  end
-end
diff --git a/app/models/concerns/account_associations.rb b/app/models/concerns/account_associations.rb
index f2a4eae77287ba49803286e12d45387565ae9135..f9e7a3bea7730b87680570ff5e9cf14c42e35936 100644
--- a/app/models/concerns/account_associations.rb
+++ b/app/models/concerns/account_associations.rb
@@ -7,8 +7,7 @@ module AccountAssociations
     # Local users
     has_one :user, inverse_of: :account, dependent: :destroy
 
-    # Identity proofs
-    has_many :identity_proofs, class_name: 'AccountIdentityProof', dependent: :destroy, inverse_of: :account
+    # E2EE
     has_many :devices, dependent: :destroy, inverse_of: :account
 
     # Timelines
diff --git a/app/models/concerns/account_merging.rb b/app/models/concerns/account_merging.rb
index 8d37c6e5679ab18cd0e0cd9374e4c2150adc5c05..119773e6b976b9ce48dd75e9462a2b59ea4c5e21 100644
--- a/app/models/concerns/account_merging.rb
+++ b/app/models/concerns/account_merging.rb
@@ -13,7 +13,7 @@ module AccountMerging
 
     owned_classes = [
       Status, StatusPin, MediaAttachment, Poll, Report, Tombstone, Favourite,
-      Follow, FollowRequest, Block, Mute, AccountIdentityProof,
+      Follow, FollowRequest, Block, Mute,
       AccountModerationNote, AccountPin, AccountStat, ListAccount,
       PollVote, Mention, AccountDeletionRequest, AccountNote, FollowRecommendationSuppression
     ]
diff --git a/app/serializers/activitypub/actor_serializer.rb b/app/serializers/activitypub/actor_serializer.rb
index a7d948976fbcc7aa66104359eb9d88d471c914ea..48707aa16c1d8aaca5cd66a39e5ed1831625b837 100644
--- a/app/serializers/activitypub/actor_serializer.rb
+++ b/app/serializers/activitypub/actor_serializer.rb
@@ -6,8 +6,7 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
   context :security
 
   context_extensions :manually_approves_followers, :featured, :also_known_as,
-                     :moved_to, :property_value, :identity_proof,
-                     :discoverable, :olm, :suspended
+                     :moved_to, :property_value, :discoverable, :olm, :suspended
 
   attributes :id, :type, :following, :followers,
              :inbox, :outbox, :featured, :featured_tags,
@@ -143,7 +142,7 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
   end
 
   def virtual_attachments
-    object.suspended? ? [] : (object.fields + object.identity_proofs.active)
+    object.suspended? ? [] : object.fields
   end
 
   def moved_to
diff --git a/app/serializers/rest/identity_proof_serializer.rb b/app/serializers/rest/identity_proof_serializer.rb
deleted file mode 100644
index 0e7415935a099dd23a971da119d248cd7c4f19c3..0000000000000000000000000000000000000000
--- a/app/serializers/rest/identity_proof_serializer.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-class REST::IdentityProofSerializer < ActiveModel::Serializer
-  attributes :provider, :provider_username, :updated_at, :proof_url, :profile_url
-
-  def proof_url
-    object.badge.proof_url
-  end
-
-  def profile_url
-    object.badge.profile_url
-  end
-
-  def provider
-    object.provider.capitalize
-  end
-end
diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb
index 4ab6912e5d409f7a77cc8065eb0493c470abfc5e..ec5140720fe0f03bcdd8a631c3b8e24cab211a7e 100644
--- a/app/services/activitypub/process_account_service.rb
+++ b/app/services/activitypub/process_account_service.rb
@@ -27,7 +27,6 @@ class ActivityPub::ProcessAccountService < BaseService
         create_account if @account.nil?
         update_account
         process_tags
-        process_attachments
 
         process_duplicate_accounts! if @options[:verified_webfinger]
       else
@@ -301,23 +300,6 @@ class ActivityPub::ProcessAccountService < BaseService
     end
   end
 
-  def process_attachments
-    return if @json['attachment'].blank?
-
-    previous_proofs = @account.identity_proofs.to_a
-    current_proofs  = []
-
-    as_array(@json['attachment']).each do |attachment|
-      next unless equals_or_includes?(attachment['type'], 'IdentityProof')
-      current_proofs << process_identity_proof(attachment)
-    end
-
-    previous_proofs.each do |previous_proof|
-      next if current_proofs.any? { |current_proof| current_proof.id == previous_proof.id }
-      previous_proof.delete
-    end
-  end
-
   def process_emoji(tag)
     return if skip_download?
     return if tag['name'].blank? || tag['icon'].blank? || tag['icon']['url'].blank?
@@ -334,12 +316,4 @@ class ActivityPub::ProcessAccountService < BaseService
     emoji.image_remote_url = image_url
     emoji.save
   end
-
-  def process_identity_proof(attachment)
-    provider          = attachment['signatureAlgorithm']
-    provider_username = attachment['name']
-    token             = attachment['signatureValue']
-
-    @account.identity_proofs.where(provider: provider, provider_username: provider_username).find_or_create_by(provider: provider, provider_username: provider_username, token: token)
-  end
 end
diff --git a/app/services/delete_account_service.rb b/app/services/delete_account_service.rb
index ac571d7e29dd375cbd47bd0af20274bd7e580412..0e3fedfe731e9780b12efe511b495fba9941443e 100644
--- a/app/services/delete_account_service.rb
+++ b/app/services/delete_account_service.rb
@@ -17,7 +17,6 @@ class DeleteAccountService < BaseService
     domain_blocks
     featured_tags
     follow_requests
-    identity_proofs
     list_accounts
     migrations
     mute_relationships
@@ -45,7 +44,6 @@ class DeleteAccountService < BaseService
     domain_blocks
     featured_tags
     follow_requests
-    identity_proofs
     list_accounts
     migrations
     mute_relationships
diff --git a/app/views/accounts/_bio.html.haml b/app/views/accounts/_bio.html.haml
index efc26d1366c5909529cf9e5f4610375a6558645e..e8a49a1aa4a653e0b630d70f182a92401f7b3a78 100644
--- a/app/views/accounts/_bio.html.haml
+++ b/app/views/accounts/_bio.html.haml
@@ -1,16 +1,8 @@
-- proofs = account.identity_proofs.active
 - fields = account.fields
 
 .public-account-bio
-  - unless fields.empty? && proofs.empty?
+  - unless fields.empty?
     .account__header__fields
-      - proofs.each do |proof|
-        %dl
-          %dt= proof.provider.capitalize
-          %dd.verified
-            = link_to fa_icon('check'), proof.badge.proof_url, class: 'verified__mark', title: t('accounts.link_verified_on', date: l(proof.updated_at))
-            = link_to proof.provider_username, proof.badge.profile_url
-
       - fields.each do |field|
         %dl
           %dt.emojify{ title: field.name }= Formatter.instance.format_field(account, field.name, custom_emojify: true)
diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml
index 66eb49342759c9616a942e9e0be5b879a0397439..2b6e28e8ddcc5d60befde0cbfa8d8db1a3a63fe4 100644
--- a/app/views/admin/accounts/show.html.haml
+++ b/app/views/admin/accounts/show.html.haml
@@ -8,20 +8,12 @@
 = render 'application/card', account: @account
 
 - account = @account
-- proofs = account.identity_proofs.active
 - fields = account.fields
-- unless fields.empty? && proofs.empty? && account.note.blank?
+- unless fields.empty? && account.note.blank?
   .admin-account-bio
-    - unless fields.empty? && proofs.empty?
+    - unless fields.empty?
       %div
         .account__header__fields
-          - proofs.each do |proof|
-            %dl
-              %dt= proof.provider.capitalize
-              %dd.verified
-                = link_to fa_icon('check'), proof.badge.proof_url, class: 'verified__mark', title: t('accounts.link_verified_on', date: l(proof.updated_at))
-                = link_to proof.provider_username, proof.badge.profile_url
-
           - fields.each do |field|
             %dl
               %dt.emojify{ title: field.name }= Formatter.instance.format_field(account, field.name, custom_emojify: true)
diff --git a/app/views/settings/identity_proofs/_proof.html.haml b/app/views/settings/identity_proofs/_proof.html.haml
deleted file mode 100644
index 14e8e91be3702b1fbc857c881e055e774199f6bf..0000000000000000000000000000000000000000
--- a/app/views/settings/identity_proofs/_proof.html.haml
+++ /dev/null
@@ -1,21 +0,0 @@
-%tr
-  %td
-    = link_to proof.badge.profile_url, class: 'name-tag' do
-      = image_tag proof.badge.avatar_url, width: 15, height: 15, alt: '', class: 'avatar'
-      %span.username
-        = proof.provider_username
-        %span= "(#{proof.provider.capitalize})"
-
-  %td
-    - if proof.live?
-      %span.positive-hint
-        = fa_icon 'check-circle fw'
-        = t('identity_proofs.active')
-    - else
-      %span.negative-hint
-        = fa_icon 'times-circle fw'
-        = t('identity_proofs.inactive')
-
-  %td
-    = table_link_to 'external-link', t('identity_proofs.view_proof'), proof.badge.proof_url if proof.badge.proof_url
-    = table_link_to 'trash', t('identity_proofs.remove'), settings_identity_proof_path(proof), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') }
diff --git a/app/views/settings/identity_proofs/index.html.haml b/app/views/settings/identity_proofs/index.html.haml
deleted file mode 100644
index d0ea03ecd0e9d53127218159d364f139fa1f97e8..0000000000000000000000000000000000000000
--- a/app/views/settings/identity_proofs/index.html.haml
+++ /dev/null
@@ -1,17 +0,0 @@
-- content_for :page_title do
-  = t('settings.identity_proofs')
-
-%p= t('identity_proofs.explanation_html')
-
-- unless @proofs.empty?
-  %hr.spacer/
-
-  .table-wrapper
-    %table.table
-      %thead
-        %tr
-          %th= t('identity_proofs.identity')
-          %th= t('identity_proofs.status')
-          %th
-      %tbody
-        = render partial: 'settings/identity_proofs/proof', collection: @proofs, as: :proof
diff --git a/app/views/settings/identity_proofs/new.html.haml b/app/views/settings/identity_proofs/new.html.haml
deleted file mode 100644
index 5e4e9895d167ba59bd80dfa4c6db46ee6421d66c..0000000000000000000000000000000000000000
--- a/app/views/settings/identity_proofs/new.html.haml
+++ /dev/null
@@ -1,36 +0,0 @@
-- content_for :page_title do
-  = t('identity_proofs.authorize_connection_prompt')
-
-.form-container
-  .oauth-prompt
-    %h2= t('identity_proofs.authorize_connection_prompt')
-
-  = simple_form_for @proof, url: settings_identity_proofs_url, html: { method: :post } do |f|
-    = f.input :provider, as: :hidden
-    = f.input :provider_username, as: :hidden
-    = f.input :token, as: :hidden
-
-    = hidden_field_tag :user_agent, params[:user_agent]
-
-    .connection-prompt
-      .connection-prompt__row.connection-prompt__connection
-        .connection-prompt__column
-          = image_tag current_account.avatar.url(:original), size: 96, class: 'account__avatar'
-
-          %p= t('identity_proofs.i_am_html', username: content_tag(:strong,current_account.username), service: site_hostname)
-
-        .connection-prompt__column.connection-prompt__column-sep
-          = fa_icon 'link'
-
-        .connection-prompt__column
-          = image_tag @proof.badge.avatar_url, size: 96, class: 'account__avatar'
-
-          %p= t('identity_proofs.i_am_html', username: content_tag(:strong, @proof.provider_username), service: @proof.provider.capitalize)
-
-    .connection-prompt__post
-      = f.input :post_status, label: t('identity_proofs.publicize_checkbox'), as: :boolean, wrapper: :with_label, :input_html => { checked: true }
-
-      = f.input :status_text, as: :text, input_html: { value: t('identity_proofs.publicize_toot', username: @proof.provider_username, service: @proof.provider.capitalize, url: @proof.badge.proof_url), rows: 4 }
-
-    = f.button :button, t('identity_proofs.authorize'), type: :submit
-    = link_to t('simple_form.no'), settings_identity_proofs_url, class: 'button negative'
diff --git a/config/locales/en.yml b/config/locales/en.yml
index c98b828015e1654fec76524d43ca1d7d050d376b..1aa96ba0f721dc111e0c3f64cb13e1febddf6164 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -985,26 +985,6 @@ en:
       other: Something isn't quite right yet! Please review %{count} errors below
   html_validator:
     invalid_markup: 'contains invalid HTML markup: %{error}'
-  identity_proofs:
-    active: Active
-    authorize: Yes, authorize
-    authorize_connection_prompt: Authorize this cryptographic connection?
-    errors:
-      failed: The cryptographic connection failed. Please try again from %{provider}.
-      keybase:
-        invalid_token: Keybase tokens are hashes of signatures and must be 66 hex characters
-        verification_failed: Keybase does not recognize this token as a signature of Keybase user %{kb_username}. Please retry from Keybase.
-      wrong_user: Cannot create a proof for %{proving} while logged in as %{current}. Log in as %{proving} and try again.
-    explanation_html: Here you can cryptographically connect your other identities from other platforms, such as Keybase. This lets other people send you encrypted messages on those platforms and allows them to trust that the content you send them comes from you.
-    i_am_html: I am %{username} on %{service}.
-    identity: Identity
-    inactive: Inactive
-    publicize_checkbox: 'And toot this:'
-    publicize_toot: 'It is proven! I am %{username} on %{service}: %{url}'
-    remove: Remove proof from account
-    removed: Successfully removed proof from account
-    status: Verification status
-    view_proof: View proof
   imports:
     errors:
       over_rows_processing_limit: contains more than %{count} rows
@@ -1279,7 +1259,6 @@ en:
     edit_profile: Edit profile
     export: Data export
     featured_tags: Featured hashtags
-    identity_proofs: Identity proofs
     import: Import
     import_and_export: Import and export
     migrate: Account migration
diff --git a/config/navigation.rb b/config/navigation.rb
index 477d1c9ffb3bc47eae7ef94f099612f9b74f4f02..99743c222aa7f901f93728e37cf06623773ab836 100644
--- a/config/navigation.rb
+++ b/config/navigation.rb
@@ -7,7 +7,6 @@ SimpleNavigation::Configuration.run do |navigation|
     n.item :profile, safe_join([fa_icon('user fw'), t('settings.profile')]), settings_profile_url, if: -> { current_user.functional? } do |s|
       s.item :profile, safe_join([fa_icon('pencil fw'), t('settings.appearance')]), settings_profile_url
       s.item :featured_tags, safe_join([fa_icon('hashtag fw'), t('settings.featured_tags')]), settings_featured_tags_url
-      s.item :identity_proofs, safe_join([fa_icon('key fw'), t('settings.identity_proofs')]), settings_identity_proofs_path, highlights_on: %r{/settings/identity_proofs*}, if: proc { current_account.identity_proofs.exists? }
     end
 
     n.item :preferences, safe_join([fa_icon('cog fw'), t('settings.preferences')]), settings_preferences_url, if: -> { current_user.functional? } do |s|
diff --git a/config/routes.rb b/config/routes.rb
index c7317d17323b15761409d8d1bb48127404c1039a..5f73129eac1cbdece2328e665368d9ff2c389dd3 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -25,7 +25,6 @@ Rails.application.routes.draw do
   get '.well-known/nodeinfo', to: 'well_known/nodeinfo#index', as: :nodeinfo, defaults: { format: 'json' }
   get '.well-known/webfinger', to: 'well_known/webfinger#show', as: :webfinger
   get '.well-known/change-password', to: redirect('/auth/edit')
-  get '.well-known/keybase-proof-config', to: 'well_known/keybase_proof_config#show'
 
   get '/nodeinfo/2.0', to: 'well_known/nodeinfo#show', as: :nodeinfo_schema
 
@@ -146,8 +145,6 @@ Rails.application.routes.draw do
       resource :confirmation, only: [:new, :create]
     end
 
-    resources :identity_proofs, only: [:index, :new, :create, :destroy]
-
     resources :applications, except: [:edit] do
       member do
         post :regenerate
@@ -332,9 +329,6 @@ Rails.application.routes.draw do
     # OEmbed
     get '/oembed', to: 'oembed#show', as: :oembed
 
-    # Identity proofs
-    get :proofs, to: 'proofs#index'
-
     # JSON / REST API
     namespace :v1 do
       resources :statuses, only: [:create, :show, :destroy] do
diff --git a/db/post_migrate/20211126000907_drop_account_identity_proofs.rb b/db/post_migrate/20211126000907_drop_account_identity_proofs.rb
new file mode 100644
index 0000000000000000000000000000000000000000..44a6f1f08c3d36cbe681fc0aa904a4c202dae887
--- /dev/null
+++ b/db/post_migrate/20211126000907_drop_account_identity_proofs.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class DropAccountIdentityProofs < ActiveRecord::Migration[5.2]
+  disable_ddl_transaction!
+
+  def up
+    drop_table :account_identity_proofs
+  end
+
+  def down
+    raise ActiveRecord::IrreversibleMigration
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 00969daf1083d29390d97f0216a413d8c5a33247..54a46730cd065f1d08d1618ac8d4c578421f39de 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: 2021_11_23_212714) do
+ActiveRecord::Schema.define(version: 2021_11_26_000907) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -51,18 +51,6 @@ ActiveRecord::Schema.define(version: 2021_11_23_212714) do
     t.index ["account_id", "domain"], name: "index_account_domain_blocks_on_account_id_and_domain", unique: true
   end
 
-  create_table "account_identity_proofs", force: :cascade do |t|
-    t.bigint "account_id"
-    t.string "provider", default: "", null: false
-    t.string "provider_username", default: "", null: false
-    t.text "token", default: "", null: false
-    t.boolean "verified", default: false, null: false
-    t.boolean "live", default: false, null: false
-    t.datetime "created_at", null: false
-    t.datetime "updated_at", null: false
-    t.index ["account_id", "provider", "provider_username"], name: "index_account_proofs_on_account_and_provider_and_username", unique: true
-  end
-
   create_table "account_migrations", force: :cascade do |t|
     t.bigint "account_id"
     t.string "acct", default: "", null: false
@@ -1010,7 +998,6 @@ ActiveRecord::Schema.define(version: 2021_11_23_212714) do
   add_foreign_key "account_conversations", "conversations", on_delete: :cascade
   add_foreign_key "account_deletion_requests", "accounts", on_delete: :cascade
   add_foreign_key "account_domain_blocks", "accounts", name: "fk_206c6029bd", on_delete: :cascade
-  add_foreign_key "account_identity_proofs", "accounts", on_delete: :cascade
   add_foreign_key "account_migrations", "accounts", column: "target_account_id", on_delete: :nullify
   add_foreign_key "account_migrations", "accounts", on_delete: :cascade
   add_foreign_key "account_moderation_notes", "accounts"
diff --git a/spec/controllers/api/proofs_controller_spec.rb b/spec/controllers/api/proofs_controller_spec.rb
deleted file mode 100644
index 2fe6150052cff37afbb900cbabc69e5bdada0151..0000000000000000000000000000000000000000
--- a/spec/controllers/api/proofs_controller_spec.rb
+++ /dev/null
@@ -1,93 +0,0 @@
-require 'rails_helper'
-
-describe Api::ProofsController do
-  let(:alice) { Fabricate(:account, username: 'alice') }
-
-  before do
-    stub_request(:get, 'https://keybase.io/_/api/1.0/sig/proof_valid.json?domain=cb6e6126.ngrok.io&kb_username=crypto_alice&sig_hash=111111111111111111111111111111111111111111111111111111111111111111&username=alice').to_return(status: 200, body: '{"proof_valid":true,"proof_live":false}')
-    stub_request(:get, 'https://keybase.io/_/api/1.0/sig/proof_live.json?domain=cb6e6126.ngrok.io&kb_username=crypto_alice&sig_hash=111111111111111111111111111111111111111111111111111111111111111111&username=alice').to_return(status: 200, body: '{"proof_valid":true,"proof_live":true}')
-    stub_request(:get, 'https://keybase.io/_/api/1.0/sig/proof_valid.json?domain=cb6e6126.ngrok.io&kb_username=hidden_alice&sig_hash=222222222222222222222222222222222222222222222222222222222222222222&username=alice').to_return(status: 200, body: '{"proof_valid":true,"proof_live":true}')
-    stub_request(:get, 'https://keybase.io/_/api/1.0/sig/proof_live.json?domain=cb6e6126.ngrok.io&kb_username=hidden_alice&sig_hash=222222222222222222222222222222222222222222222222222222222222222222&username=alice').to_return(status: 200, body: '{"proof_valid":true,"proof_live":true}')
-  end
-
-  describe 'GET #index' do
-    describe 'with a non-existent username' do
-      it '404s' do
-        get :index, params: { username: 'nonexistent', provider: 'keybase' }
-
-        expect(response).to have_http_status(:not_found)
-      end
-    end
-
-    describe 'with a user that has no proofs' do
-      it 'is an empty list of signatures' do
-        get :index, params: { username: alice.username, provider: 'keybase' }
-
-        expect(body_as_json[:signatures]).to eq []
-      end
-    end
-
-    describe 'with a user that has a live, valid proof' do
-      let(:token1) { '111111111111111111111111111111111111111111111111111111111111111111' }
-      let(:kb_name1) { 'crypto_alice' }
-
-      before do
-        Fabricate(:account_identity_proof, account: alice, verified: true, live: true, token: token1, provider_username: kb_name1)
-      end
-
-      it 'is a list with that proof in it' do
-        get :index, params: { username: alice.username, provider: 'keybase' }
-
-        expect(body_as_json[:signatures]).to eq [
-          { kb_username: kb_name1, sig_hash: token1 },
-        ]
-      end
-
-      describe 'add one that is neither live nor valid' do
-        let(:token2) { '222222222222222222222222222222222222222222222222222222222222222222' }
-        let(:kb_name2) { 'hidden_alice' }
-
-        before do
-          Fabricate(:account_identity_proof, account: alice, verified: false, live: false, token: token2, provider_username: kb_name2)
-        end
-
-        it 'is a list with both proofs' do
-          get :index, params: { username: alice.username, provider: 'keybase' }
-
-          expect(body_as_json[:signatures]).to eq [
-            { kb_username: kb_name1, sig_hash: token1 },
-            { kb_username: kb_name2, sig_hash: token2 },
-          ]
-        end
-      end
-    end
-
-    describe 'a user that has an avatar' do
-      let(:alice) { Fabricate(:account, username: 'alice', avatar: attachment_fixture('avatar.gif')) }
-
-      context 'and a proof' do
-        let(:token1) { '111111111111111111111111111111111111111111111111111111111111111111' }
-        let(:kb_name1) { 'crypto_alice' }
-
-        before do
-          Fabricate(:account_identity_proof, account: alice, verified: true, live: true, token: token1, provider_username: kb_name1)
-          get :index, params: { username: alice.username, provider: 'keybase' }
-        end
-
-        it 'has two keys: signatures and avatar' do
-          expect(body_as_json.keys).to match_array [:signatures, :avatar]
-        end
-
-        it 'has the correct signatures' do
-          expect(body_as_json[:signatures]).to eq [
-            { kb_username: kb_name1, sig_hash: token1 },
-          ]
-        end
-
-        it 'has the correct avatar url' do
-          expect(body_as_json[:avatar]).to match "https://cb6e6126.ngrok.io#{alice.avatar.url}"
-        end
-      end
-    end
-  end
-end
diff --git a/spec/controllers/settings/identity_proofs_controller_spec.rb b/spec/controllers/settings/identity_proofs_controller_spec.rb
deleted file mode 100644
index 16f2362278ac706ab1e28e461c165768ac04f04c..0000000000000000000000000000000000000000
--- a/spec/controllers/settings/identity_proofs_controller_spec.rb
+++ /dev/null
@@ -1,186 +0,0 @@
-require 'rails_helper'
-
-describe Settings::IdentityProofsController do
-  include RoutingHelper
-  render_views
-
-  let(:user) { Fabricate(:user) }
-  let(:valid_token) { '1'*66 }
-  let(:kbname) { 'kbuser' }
-  let(:provider) { 'keybase' }
-  let(:findable_id) { Faker::Number.number(digits: 5) }
-  let(:unfindable_id) { Faker::Number.number(digits: 5) }
-  let(:new_proof_params) do
-    { provider: provider, provider_username: kbname, token: valid_token, username: user.account.username }
-  end
-  let(:status_text) { "i just proved that i am also #{kbname} on #{provider}." }
-  let(:status_posting_params) do
-    { post_status: '0', status_text: status_text }
-  end
-  let(:postable_params) do
-    { account_identity_proof: new_proof_params.merge(status_posting_params) }
-  end
-
-  before do
-    allow_any_instance_of(ProofProvider::Keybase::Verifier).to receive(:status) { { 'proof_valid' => true, 'proof_live' => true } }
-    sign_in user, scope: :user
-  end
-
-  describe 'new proof creation' do
-    context 'GET #new' do
-      before do
-        allow_any_instance_of(ProofProvider::Keybase::Badge).to receive(:avatar_url) { full_pack_url('media/images/void.png') }
-      end
-
-      context 'with all of the correct params' do
-        it 'renders the template' do
-          get :new, params: new_proof_params
-          expect(response).to render_template(:new)
-        end
-      end
-
-      context 'without any params' do
-        it 'redirects to :index' do
-          get :new, params: {}
-          expect(response).to redirect_to settings_identity_proofs_path
-        end
-      end
-
-      context 'with params to prove a different, not logged-in user' do
-        let(:wrong_user_params) { new_proof_params.merge(username: 'someone_else') }
-
-        it 'shows a helpful alert' do
-          get :new, params: wrong_user_params
-          expect(flash[:alert]).to eq I18n.t('identity_proofs.errors.wrong_user', proving: 'someone_else', current: user.account.username)
-        end
-      end
-
-      context 'with params to prove the same username cased differently' do
-        let(:capitalized_username) { new_proof_params.merge(username: user.account.username.upcase) }
-
-        it 'renders the new template' do
-          get :new, params: capitalized_username
-          expect(response).to render_template(:new)
-        end
-      end
-    end
-
-    context 'POST #create' do
-      context 'when saving works' do
-        before do
-          allow(ProofProvider::Keybase::Worker).to receive(:perform_async)
-          allow_any_instance_of(ProofProvider::Keybase::Verifier).to receive(:valid?) { true }
-          allow_any_instance_of(AccountIdentityProof).to receive(:on_success_path) { root_url }
-        end
-
-        it 'serializes a ProofProvider::Keybase::Worker' do
-          expect(ProofProvider::Keybase::Worker).to receive(:perform_async)
-          post :create, params: postable_params
-        end
-
-        it 'delegates redirection to the proof provider' do
-          expect_any_instance_of(AccountIdentityProof).to receive(:on_success_path)
-          post :create, params: postable_params
-          expect(response).to redirect_to root_url
-        end
-
-        it 'does not post a status' do
-          expect(PostStatusService).not_to receive(:new)
-          post :create, params: postable_params
-        end
-
-        context 'and the user has requested to post a status' do
-          let(:postable_params_with_status) do
-            postable_params.tap { |p| p[:account_identity_proof][:post_status] = '1' }
-          end
-
-          it 'posts a status' do
-            expect_any_instance_of(PostStatusService).to receive(:call).with(user.account, text: status_text)
-
-            post :create, params: postable_params_with_status
-          end
-        end
-      end
-
-      context 'when saving fails' do
-        before do
-          allow_any_instance_of(ProofProvider::Keybase::Verifier).to receive(:valid?) { false }
-        end
-
-        it 'redirects to :index' do
-          post :create, params: postable_params
-          expect(response).to redirect_to settings_identity_proofs_path
-        end
-
-        it 'flashes a helpful message' do
-          post :create, params: postable_params
-          expect(flash[:alert]).to eq I18n.t('identity_proofs.errors.failed', provider: 'Keybase')
-        end
-      end
-
-      context 'it can also do an update if the provider and username match an existing proof' do
-        before do
-          allow_any_instance_of(ProofProvider::Keybase::Verifier).to receive(:valid?) { true }
-          allow(ProofProvider::Keybase::Worker).to receive(:perform_async)
-          Fabricate(:account_identity_proof, account: user.account, provider: provider, provider_username: kbname)
-          allow_any_instance_of(AccountIdentityProof).to receive(:on_success_path) { root_url }
-        end
-
-        it 'calls update with the new token' do
-          expect_any_instance_of(AccountIdentityProof).to receive(:save) do |proof|
-            expect(proof.token).to eq valid_token
-          end
-
-          post :create, params: postable_params
-        end
-      end
-    end
-  end
-
-  describe 'GET #index' do
-    context 'with no existing proofs' do
-      it 'shows the helpful explanation' do
-        get :index
-        expect(response.body).to match I18n.t('identity_proofs.explanation_html')
-      end
-    end
-
-    context 'with two proofs' do
-      before do
-        allow_any_instance_of(ProofProvider::Keybase::Verifier).to receive(:valid?) { true }
-        @proof1 = Fabricate(:account_identity_proof, account: user.account)
-        @proof2 = Fabricate(:account_identity_proof, account: user.account)
-        allow_any_instance_of(AccountIdentityProof).to receive(:badge) { double(avatar_url: '', profile_url: '', proof_url: '') }
-        allow_any_instance_of(AccountIdentityProof).to receive(:refresh!) {}
-      end
-
-      it 'has the first proof username on the page' do
-        get :index
-        expect(response.body).to match /#{Regexp.quote(@proof1.provider_username)}/
-      end
-
-      it 'has the second proof username on the page' do
-        get :index
-        expect(response.body).to match /#{Regexp.quote(@proof2.provider_username)}/
-      end
-    end
-  end
-
-  describe 'DELETE #destroy' do
-    before do
-      allow_any_instance_of(ProofProvider::Keybase::Verifier).to receive(:valid?) { true }
-      @proof1 = Fabricate(:account_identity_proof, account: user.account)
-      allow_any_instance_of(AccountIdentityProof).to receive(:badge) { double(avatar_url: '', profile_url: '', proof_url: '') }
-      allow_any_instance_of(AccountIdentityProof).to receive(:refresh!) {}
-      delete :destroy, params: { id: @proof1.id }
-    end
-
-    it 'redirects to :index' do
-      expect(response).to redirect_to settings_identity_proofs_path
-    end
-
-    it 'removes the proof' do
-      expect(AccountIdentityProof.where(id: @proof1.id).count).to eq 0
-    end
-  end
-end
diff --git a/spec/controllers/well_known/keybase_proof_config_controller_spec.rb b/spec/controllers/well_known/keybase_proof_config_controller_spec.rb
deleted file mode 100644
index 00f251c3c67554d3fb423518413e67d00e3d9657..0000000000000000000000000000000000000000
--- a/spec/controllers/well_known/keybase_proof_config_controller_spec.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require 'rails_helper'
-
-describe WellKnown::KeybaseProofConfigController, type: :controller do
-  render_views
-
-  describe 'GET #show' do
-    it 'renders json' do
-      get :show
-
-      expect(response).to have_http_status(200)
-      expect(response.media_type).to eq 'application/json'
-      expect { JSON.parse(response.body) }.not_to raise_exception
-    end
-  end
-end
diff --git a/spec/fabricators/account_identity_proof_fabricator.rb b/spec/fabricators/account_identity_proof_fabricator.rb
deleted file mode 100644
index 7b932fa968792347974219f1b28536f67f8194ce..0000000000000000000000000000000000000000
--- a/spec/fabricators/account_identity_proof_fabricator.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-Fabricator(:account_identity_proof) do
-  account
-  provider 'keybase'
-  provider_username { sequence(:provider_username) { |i| "#{Faker::Lorem.characters(number: 15)}" } }
-  token { sequence(:token) { |i| "#{i}#{Faker::Crypto.sha1()*2}"[0..65] } }
-  verified false
-  live false
-end
diff --git a/spec/lib/proof_provider/keybase/verifier_spec.rb b/spec/lib/proof_provider/keybase/verifier_spec.rb
deleted file mode 100644
index 0081a735df0ca28925fbe4d2eb596c3f41afd27c..0000000000000000000000000000000000000000
--- a/spec/lib/proof_provider/keybase/verifier_spec.rb
+++ /dev/null
@@ -1,82 +0,0 @@
-require 'rails_helper'
-
-describe ProofProvider::Keybase::Verifier do
-  let(:my_domain) { Rails.configuration.x.local_domain }
-
-  let(:keybase_proof) do
-    local_proof = AccountIdentityProof.new(
-      provider: 'Keybase',
-      provider_username: 'cryptoalice',
-      token: '11111111111111111111111111'
-    )
-
-    described_class.new('alice', 'cryptoalice', '11111111111111111111111111', my_domain)
-  end
-
-  let(:query_params) do
-    "domain=#{my_domain}&kb_username=cryptoalice&sig_hash=11111111111111111111111111&username=alice"
-  end
-
-  describe '#valid?' do
-    let(:base_url) { 'https://keybase.io/_/api/1.0/sig/proof_valid.json' }
-
-    context 'when valid' do
-      before do
-        json_response_body = '{"status":{"code":0,"name":"OK"},"proof_valid":true}'
-        stub_request(:get, "#{base_url}?#{query_params}").to_return(status: 200, body: json_response_body)
-      end
-
-      it 'calls out to keybase and returns true' do
-        expect(keybase_proof.valid?).to eq true
-      end
-    end
-
-    context 'when invalid' do
-      before do
-        json_response_body = '{"status":{"code":0,"name":"OK"},"proof_valid":false}'
-        stub_request(:get, "#{base_url}?#{query_params}").to_return(status: 200, body: json_response_body)
-      end
-
-      it 'calls out to keybase and returns false' do
-        expect(keybase_proof.valid?).to eq false
-      end
-    end
-
-    context 'with an unexpected api response' do
-      before do
-        json_response_body = '{"status":{"code":100,"desc":"wrong size hex_id","fields":{"sig_hash":"wrong size hex_id"},"name":"INPUT_ERROR"}}'
-        stub_request(:get, "#{base_url}?#{query_params}").to_return(status: 200, body: json_response_body)
-      end
-
-      it 'swallows the error and returns false' do
-        expect(keybase_proof.valid?).to eq false
-      end
-    end
-  end
-
-  describe '#status' do
-    let(:base_url) { 'https://keybase.io/_/api/1.0/sig/proof_live.json' }
-
-    context 'with a normal response' do
-      before do
-        json_response_body = '{"status":{"code":0,"name":"OK"},"proof_live":false,"proof_valid":true}'
-        stub_request(:get, "#{base_url}?#{query_params}").to_return(status: 200, body: json_response_body)
-      end
-
-      it 'calls out to keybase and returns the status fields as proof_valid and proof_live' do
-        expect(keybase_proof.status).to include({ 'proof_valid' => true, 'proof_live' => false })
-      end
-    end
-
-    context 'with an unexpected keybase response' do
-      before do
-        json_response_body = '{"status":{"code":100,"desc":"missing non-optional field sig_hash","fields":{"sig_hash":"missing non-optional field sig_hash"},"name":"INPUT_ERROR"}}'
-        stub_request(:get, "#{base_url}?#{query_params}").to_return(status: 200, body: json_response_body)
-      end
-
-      it 'raises a ProofProvider::Keybase::UnexpectedResponseError' do
-        expect { keybase_proof.status }.to raise_error ProofProvider::Keybase::UnexpectedResponseError
-      end
-    end
-  end
-end
diff --git a/spec/services/activitypub/process_account_service_spec.rb b/spec/services/activitypub/process_account_service_spec.rb
index 1b1d878a7e6b3018c2d1db554e26cd73c5d39c0f..7728b9ba829e22367a19fe12670999c64e7b37dc 100644
--- a/spec/services/activitypub/process_account_service_spec.rb
+++ b/spec/services/activitypub/process_account_service_spec.rb
@@ -30,51 +30,6 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do
     end
   end
 
-  context 'identity proofs' do
-    let(:payload) do
-      {
-        id: 'https://foo.test',
-        type: 'Actor',
-        inbox: 'https://foo.test/inbox',
-        attachment: [
-          { type: 'IdentityProof', name: 'Alice', signatureAlgorithm: 'keybase', signatureValue: 'a' * 66 },
-        ],
-      }.with_indifferent_access
-    end
-
-    it 'parses out of attachment' do
-      allow(ProofProvider::Keybase::Worker).to receive(:perform_async)
-
-      account = subject.call('alice', 'example.com', payload)
-
-      expect(account.identity_proofs.count).to eq 1
-
-      proof = account.identity_proofs.first
-
-      expect(proof.provider).to eq 'keybase'
-      expect(proof.provider_username).to eq 'Alice'
-      expect(proof.token).to eq 'a' * 66
-    end
-
-    it 'removes no longer present proofs' do
-      allow(ProofProvider::Keybase::Worker).to receive(:perform_async)
-
-      account   = Fabricate(:account, username: 'alice', domain: 'example.com')
-      old_proof = Fabricate(:account_identity_proof, account: account, provider: 'keybase', provider_username: 'Bob', token: 'b' * 66)
-
-      subject.call('alice', 'example.com', payload)
-
-      expect(account.identity_proofs.count).to eq 1
-      expect(account.identity_proofs.find_by(id: old_proof.id)).to be_nil
-    end
-
-    it 'queues a validity check on the proof' do
-      allow(ProofProvider::Keybase::Worker).to receive(:perform_async)
-      account = subject.call('alice', 'example.com', payload)
-      expect(ProofProvider::Keybase::Worker).to have_received(:perform_async)
-    end
-  end
-
   context 'when account is not suspended' do
     let!(:account) { Fabricate(:account, username: 'alice', domain: 'example.com') }