Keybase adds documentation for proof integrations

a.k.a. Keybase’s brand new Proof Integration Guide

The most basic idea behind Keybase is that it’s a protocol and directory for
connecting people’s identities and keys together. For example, it is
cryptographically proven that Keybase user tammy is all of these

Thanks to tammy for letting us use her happy profile as an example in the app store

If you know @tammycamp on Twitter, Keybase
can provide you her keys and proof that they are in fact her keys. And, going
in the other direction, you can start with something she signed, and know for
certain it was signed by Twitter user @tammycamp.

Today we are opening up a set of steps that your app or website can follow to
get cryptographic connections to Keybase. In other words, your service can join
Reddit, Twitter, Github, and Facebook on Keybase profiles. Also, Keybase
profiles will appear on the profiles of your service, allowing users to know
how to reach each other securely.

This short guide is a walkthrough of the endpoints you’ll need to implement to
make it happen. It’s not much work.

Getting started

As you read this guide, you’ll go through:

  • Proof creation; this is when the user signs a
    statement in Keybase, claiming to be a member of your service
  • Proof checking; the proof signature can be requested
    from your service, and cryptographically checked by anyone
  • Linking; you and Keybase will link to each other,
    but only upon a successful proof

In this guide, let’s call your service the Identity Service, though we both
know it’s so much more. Integrating with Keybase as an identity service
involves implementing some product features, a couple API endpoints, and then
talking to us. You’ll also send us a config file (described below) which we’ll
use to finish out the integration.

To help with your integration we have a Python reference
if you prefer
to consume the material that way and a verification
for testing things

Throughout this document our examples are written using
httpie for simplicity.

1. Config

Let’s start with the config file, which has all the information Keybase needs
to know about the Identity Service. It presents a high-level view of the
integration. All of the fields are required unless noted as optional, and all
endpoints must be HTTPS.

  "version": 1,
  "domain": "",
  "display_name": "Bee Activists",

  "username": {
    "re": "^[a-zA-Z0-9_]{2,20}$",
    "min": 2,
    "max": 20

  "brand_color": "#FFB800",

  "logo": {
    "svg_black": "",
    "svg_full": ""

  "description": "Next gen social network using big data & AI in the cloud 🤖☁️.",

  "prefill_url": "{kb_username}&username=%{username}&token=%{sig_hash}&kb_ua=%{kb_ua}",

  "profile_url": "{username}",

  "check_url": "{username}",
  "check_path": ["signatures"], 
  "avatar_path": ["avatar"], 

  "contact": ["", "sassybedazzle@keybase"]

1.1 Config Validation

Before sending us the config you should validate that it is correct using the
following endpoint. You can pass the config directly in a config param or
host the config file and pass config_url:

$ http POST "" config="$(jq -c . < config.json)"
    "status": {
        "code": 100,
        "desc": "missing or invalid inputs {"domain":"field is required"}",
        "fields": {
            "config": "missing or invalid inputs {"domain":"field is required"}"
        "name": "INPUT_ERROR"
$ http GET "" config_url=""
    "status": {
        "code": 0,
        "name": "OK"

To send us the config, you can send us the public URL for your config file or
attach it directly in a Keybase chat message to
@mlsteele or email In
our example the file is hosted at

2. Protocol

Before getting into the nitty-gritty of the protocol, here is the overall flow of
requests between the Keybase clients, Keybase server, and the Identity Service.

2.1 Flow Overview

For this example we’ll use the values in the config file defined above with an
example user: josavesbees on the beeactivists service and joans on Keybase.
A normal user would do this in the GUI of the Keybase app, but it’s easier to
see from the command line.

  1. Joan creates a proof statement with the Keybase client claiming
    ownership of the josavesbees account on BeeActivists. The Keybase
    server immediately stores this statement in joans‘s signature
    and asks them to publicly post the statement to
$ keybase prove josavesbees
Please click on the following link to post to beeactivists:

  1. Joan visits the link at They are either already logged
    in as josavesbees, or must login. Then they are presented their Keybase
    username and reminded their beeactivists username. If the 2 usernames are what
    they wish to connect, they just click “Yes, authorize” to finish the connection.

  2. Before actually saving this data, beeactivists makes a request to Keybase to
    verify that the sig_hash is valid for this combination of kb_username,
    username, and domain. If valid, beeactivists persists the signature (e.g. in a
    relational database).

  3. The signature should now be served in machine readable form
    via the check_url endpoint from the config. It should also be seen in
    human-readable form (with a link to the Keybase website) via the profile_url

Now let’s drill into the back-and-forth communication for each request.

2.2. Proof Creation

This process needs to start from Keybase so we can prepare the proof for the
Identity Service to host.

  1. From Keybase, the user creates a proof and clicks a link that opens a web
    browser into the Identity Service. Inside the link, Keybase embeds the hash of
    the proof signature and the user’s Keybase username. The user needs to be
    logged into the Identity Service at this time, so please redirect to a login
    form and back if necessary.
  2. The user reviews that both their beeactivists username and their Keybase
    usernames are correct, then authorizes the cryptographic connection.

The user has to authorize the cryptographic connection between the two identities.

If josavesbees isn’t logged in on beesactivists, they should be redirected to a login screen.

  1. The Identity Service validates the signature with Keybase and saves this
    data so it can be served during the Proof Checking flow
    described below.

Keybase creates the link via the prefill_url in the config, and here’s
everything you need to know:

  1. This proof should be stored forever unless it is updated or deleted by the user.
  2. We allow a user of the Identity Service to claim multiple Keybase accounts
    although a Keybase user may only prove one profile at at time on a given
  3. The creation endpoint must be accessible via HTTPS on the domain or a
    subdomain of the Identity Service.
  4. The prefill_url must have four fields for Keybase to fill in, the Keybase
    username, the Identity service’s username, signature hash ( a ~66 character hex
    string), and a user agent field. The user agent is used to redirect the client
    appropriately after successful proof creation but doesn’t need be stored. We
    recommend something simple like{kb_username}&username=%{username}&token=%{sig_hash}&kb_ua=%{kb_ua}.

Here’s an example of some code that might work to create a new proof. Most of
this would normally happen in a browser, but we’ll show it in Python for extreme explicitness. This user is
josavesbees on BeeActivists and joans on Keybase.

import requests

session = requests.session()
login_url = ""
result = session.get(login_url)
data = {
    "user[email]": "",
    "user[password]": "hunter2",
resp =, data=data, headers=dict(referrer=login_url))

prefill_url = ""
resp = session.get(prefill_url)

data = {
    "remote_username": "joans",
    "token": "90b0ef50119e69063d3a96625195a5ea895071debbb50a111ddde2eba9d4ecf40f",
    "kb_ua": "darwin:3.0.0",
resp ="", data=data)

When validating the new proof, the identity service should call the
sig/proof_valid endpoint as mentioned.

$ http GET
{ "proof_valid" : true }

If a user tries to post a proof and the Keybase API responds that proof_valid=false,
the Identity Service should reject this proof. When calling Keybase verifies
the signature preventing:

  1. A user from posting an invalid signature
  2. A valid signature for a different service
  3. A user from claiming the wrong account on either service

2.2.1 Creation Redirect

If the proof is valid, once the data is persisted by the Identity Service, the
user should be redirected back to Keybase via the following URL:`

Keybase will then redirect the user back into the app or the appropriate web page.

2.3. Proof Checking

Keybase needs to check the proof periodically on the Identity Service to ensure
that the user has not removed it. Similarly, the Identity Service may want to
know if the user has revoked the proof on Keybase. For this reason, there is a
protocol for both services to request the current state from one another. Keybase
will follow it, but we consider it optional for an Identity Service.

2.3.1 Keybase Will Regularly Check Proofs On The Identity Service

The proof-checking endpoint on the Identity Service should return JSON that
includes a list of the Keybase usernames (and signature hashes) that the Identity
Service user has claimed. This is exactly the same information that was created above.

The check_url in the config can be any HTTPS endpoint on the Identity Service
domain with a spot to fill in the Identity Service username. It’ll be easier if
this is a new endpoint, but we’ll look at examples of both cases.

A New Endpoint Just for Keybase Proof Checking (the Happy Path)

Here’s a check for josavesbees of Bee Activists, who has two verified
Keybase accounts.

The check_url in the config:{username}

The response from the endpoint:

$ http GET
  "signatures": [
      "kb_username": "joans",
      "sig_hash": "90b0ef50119e69063d3a96625195a5ea895071debbb50a111ddde2eba9d4ecf40f"
      "kb_username": "joans_school",
      "sig_hash": "a2934c38dd76f01934e29d533b72ab787688f5392902225788c44af0e95c1c370f"

And the check_path:


An Existing Endpoint With Keybase Proofs Added Somewhere (Still Happy, Just a Little More Work)

Here’s the same check with a shared endpoint.

The check_url in the config:{username}/attestations.json

The response from the endpoint:

$ http GET
  "attestations": [
      "verified": {
        "something": "that keybase doesnt care about"
      "verified": {
        "another thing": "that keybase doesnt care about"
      "verified": {
        "kb123": [
            "kb_username": "joans",
            "sig_hash": "90b0ef50119e69063d3a96625195a5ea895071debbb50a111ddde2eba9d4ecf40f"
            "kb_username": "joans_school",
            "sig_hash": "a2934c38dd76f01934e29d533b72ab787688f5392902225788c44af0e95c1c370f"
      "verified": {
        "final thing": "that keybase doesnt care about"
  "avatar": ""

And the check_path:

["attestations", 2, "verified", "kb123"]

The endpoint and JSON path shouldn’t change without notice, because there might
be older Keybase clients in the wild checking for it. It will also be used by
Keybase to determine whether or not the Identity Service is up, and whether or
not the Keybase user exists in the Identity Service. If the endpoint must be
changed, you can update your config and contact us to propagate the change.

Keybase clients and servers will always send the header Accept:

If a username does not exist, then the endpoint must return a 404 status code.
This endpoint is be used during the proof creation process (2.2) to determine
whether the user exists.

$ http GET
-> status 404

If a user exists but has no Keybase proof claims, then the check_url endpoint
must return a 200 response with an empty list.

$ http GET
  "signatures": [],
  "avatar": ""

Keybase clients check all of the proofs of all of the users they interact with
at most once-per-day. So you can expect load correlated with the number of
proofs and related activity on Keybase.


The check_proof endpoint may return the user’s avatar. The avatar is shown to
the user while they enter a username in the app. Responding with an avatar is
encouraged, but optional.

$ http GET
  "signatures": [],

And the avatar_path:


2.3.2 The Identity Service May Optionally Check Proofs On Keybase After Creation

We have exposed an endpoint that looks very similar to the proof_valid endpoint
above, except this one additionally verifies that the proof is live on the
Identity Service. This should only be called after the proof has been created

$ http GET
{ "proof_live" : true, "proof_valid" : true }

When calling Keybase checks
that the signature for this user is valid on Keybase and accessible on BeeActivists.
More info about accessibility and linking at
linking user profile section.

BeeActivists, to create a better experience for its users, may want to persist
and periodically update 2 fields per proof that track whether or not Keybase
thinks the proof is valid and live. This would enable BeeActivists to, for example,
hide the link on the user profile page to Keybase if it’s not currently live. If the
proof was once valid and is now not, then it has likely been revoked. This means it
will never again become live, so you needn’t bother retrying.

2.4 Proof Deletion

A proof can be deleted in two ways:

  1. The user deletes the proof from Identity Service. This causes the periodic
    check by Keybase clients and server to fail, and the proof will show as failing
    on that user’s Keybase profile. There will also be indications of the failure
    to other users tracking them.
  2. The user revokes a signature through Keybase (rendering it invalid). Keybase
    clients will ignore the revoked proof and, if the Identity Service is
    periodically checking proof liveness on Keybase, it will also be able to hide
    them. If the Identity Service is displaying badges,
    then the proof will be marked as “Revoked.”

3. Linking User Profiles

In addition to the proof creation and checking flows, one other step is
part of the integration: a link on the Identity Service to the corresponding
verified Keybase profile. Keybase will also link to the user’s profile on the
Identity Service on their Keybase profile.

The URL format to link to Keybase profiles via a specific signature is:{kb_username}/sigs/%{sig_hash}.

For our running example this could look like:

<a href="">
    @joans on keybase

Additionally, you can optionally include a badge: a small SVG image provided by
the Keybase server that displays the proof’s status. With the badge, if the
user deletes the proof on Keybase but not on the the Identity Service, visitors
can tell that the proof is no longer valid at a glance.

The endpoint for a proof’s badge is:{kb_username}/proof_badge/%{sig_hash}?
In addition to the plaintext link above, you can add:

<a href="">
    <img alt="Keybase proof status" src="">

A successful proof has a badge like this:

Keybase proof status: ok
keybasekeybaseproof okproof ok

If you don’t want to use the badge or you want to hide failing proofs instead
of showing a failing badge, you can use the sig/proof_live endpoint detailed in
the Proof Checking section.

4. Steps to Rollout

  1. Implement the proof-creation flow
  2. Implement the proof-checking flow
  3. Add a link on your users’ profile screens if
    they’ve claimed a Keybase account, and if Keybase agrees that the signature is
  4. Verify your config with the validation endpoint
  5. Send the config file or public URL as a chat message to
    @mlsteele or email and we’ll
    flip the rest of the switches on our end.

5. Resources

  1. Python reference implementation
    and drop-in Django library
  2. Verification script
  3. Keybase Logo

6. Getting in touch

If you’re ready to turn on the integration, have questions about the process or
feedback for making integrations easier, send a message to
@mlsteele on Keybase.

Leave a Reply

Your email address will not be published. Required fields are marked *

Next Post

Dagen H: The Day Sweden Switched Traffic Sides

Thu Apr 11 , 2019

You May Like