Keytrace has just launched. Read more on the blog.

atproto.identity.v1 · cryptographically signed

One identity,
many proofs.

Keytrace links your atproto handle to the accounts you already own — GitHub, DNS, npm, Mastodon, PGP — with signed, portable proofs. You be you, everywhere.

View example profile →
supports:
keytrace.dev/add/github
live
linking orta.io@orta on GitHub
resolving DID for orta.io
did:plc:t732otzqvkch7zz5d37537ry
··fetching gist gist.github.com/orta/b7dccdfb...
··matching proof token against handle
proof valid · signature matches DID
··writing dev.keytrace.claim to your PDS
§1 How it works

A proof is a link you control, connecting your atproto identity.

01

Sign in with your internet handle

OAuth with your atproto PDS. No new account. No PGP keys to generate.

02

Post a proof where you already are

Drop a short token into a gist, a DNS TXT record, a package README, a pinned post.

03

Keytrace signs the claim

Our runner verifies the proof and writes a dev.keytrace.claim record to your repo.

§2 Recent proofs

Fresh proofs from the network.

Every claim here was written to its author's PDS. Stored in their repo, verifiable by anyone.

dev.keytrace.claim12 recent claims
22h ago
olivierbriraud.eurosky.socialOlivier Briraud
signed
1d ago
antoniopinto.eurosky.socialantoniopinto.eu
signed
2d ago
patrickkrueger.comPatrick Krueger
signed
2d ago
andreijiroh.devandreijirohdev-cs50x
signed
3d ago
wnpl.destefanwinopal.com
signed
3d ago
raquelpimentel.eurosky.socialRaquel Pimentel
signed
4d ago
rickymoorhouse.ukPGP Key
signed
6d ago
laetitea.bsky.sociallaetitea.tngl.sh
signed
1w ago
chronotope.aramzs.xyzChronotope
signed
1w ago
adamspiers.orgaspiers
signed
1w ago
twoslashes.comNick Tabick
signed
1w ago
narendasan.comNaren Sivagnanadasan
signed
··· from keytrace.devlearn about the lexicon →
§3 For atproto developers

Same lexicon. Same libraries.
Your identity layer.

Keytrace is built on dev.keytrace.* — an open atproto lexicon. Anything Keytrace can read, your app can read. Everything we verify, you can re-verify. Nothing is locked behind our API.

Open lexicons
Schemas published in the orta/keytrace monorepo.
Run the runner
Use @keytrace/runner in your own projects to verify claims. npmx →
Portable across PDSes
Records live in the user's repo. Keytrace is not a silo.
dev.keytrace.claim.json json
{
  "lexicon": 1,
  "id": "dev.keytrace.claim",
  "defs": {
    "main": {
      "type": "record",
      "key": "tid",
      "description": "An identity claim linking this DID to an external account",
      "record": {
        "type": "object",
        "required": [
          "type",
          "claimUri",
          "identity",
          "sigs",
          "createdAt"
        ],
        "properties": {
          "type": {
            "type": "string",
            "knownValues": [
              "github",
              "dns",
              "activitypub",
              "bsky",
              "npm",
              "tangled",
              "pgp",
              "twitter",
              "linkedin",
              "instagram",
              "reddit",
              "hackernews",
              "orcid",
              "itchio",
              "discord",
              "steam"
            ],
            "description": "The claim type identifier"
          },
          "claimUri": {
            "type": "string",
            "description": "The identity claim URI (e.g., for github: https://gist.github.com/username/id, dns:example.com)"
          },
          "identity": {
            "type": "ref",
            "ref": "#identity",
            "description": "Structured data about the claimed identity, derived from the claimUri and knowledge inside the verification process. This is not the raw claim data, but a normalized set of fields that can be used for display and more."
          },
          "sigs": {
            "type": "array",
            "items": {
              "type": "ref",
              "ref": "dev.keytrace.signature#main"
            },
            "description": "Cryptographic attestation signatures from verification services, for example from @keytrace.dev. These are optional, and so you can pass an empty array. Keytrace will place its attestation signature in the claim here so that the library @keytrace/claims can be used by external developers to not have to implement their own verification logic."
          },
          "comment": {
            "type": "string",
            "maxLength": 256,
            "description": "Optional user-provided label for this claim"
          },
          "status": {
            "type": "string",
            "description": "Current verification status of this claim. Absent on legacy records, treated as 'verified'.",
            "knownValues": [
              "verified",
              "failed",
              "retracted"
            ]
          },
          "lastVerifiedAt": {
            "type": "string",
            "format": "datetime",
            "description": "Timestamp of the most recent successful re-verification by the system"
          },
          "failedAt": {
            "type": "string",
            "format": "datetime",
            "description": "Timestamp when the claim last failed re-verification or was retracted"
          },
          "createdAt": {
            "type": "string",
            "format": "datetime",
            "description": "Datetime when this claim was created (ISO 8601)."
          },
          "nonce": {
            "type": "string",
            "maxGraphemes": 128,
            "description": "Random one-time value embedded in the challenge text posted to the external service. Used by verifiers to confirm the proof was created specifically for this claim session. Keytrace itself does not use this, but other services making claims may require it for their verification process."
          },
          "prerelease": {
            "type": "boolean",
            "description": "Whether this claim was created during the prerelease/alpha period"
          },
          "retractedAt": {
            "type": "string",
            "format": "datetime",
            "description": "Datetime when this claim was retracted. Present only if the claim has been retracted (ISO 8601)."
          }
        }
      }
    },
    "identity": {
      "type": "object",
      "description": "Generic identity data for the claimed account",
      "required": [
        "subject"
      ],
      "properties": {
        "subject": {
          "type": "string",
          "description": "Primary identifier (username, domain, handle, etc.)"
        },
        "avatarUrl": {
          "type": "string",
          "format": "uri",
          "description": "Avatar/profile image URL"
        },
        "profileUrl": {
          "type": "string",
          "format": "uri",
          "description": "Profile page URL"
        },
        "displayName": {
          "type": "string",
          "description": "Display name if different from subject"
        }
      }
    }
  }
}
Model the claim yourself. The whole schema lives in an open repo.github.com/orta/keytrace/lexicon
§4 — /signup

Keep the handle.
Prove the rest.

Two minutes, one proof, one signed claim in your repo.

Read the launch post →