Skip to main content
API Preview
Developers

Build on the ~handle protocol

The tilde-handle is the individual identity primitive for the ~alter protocol. This page is a how-to: it teaches the principles behind the protocol and shows you how to apply them in your own stack. Part one is what you can build against today. Part two is how to implement the open protocol on your own infrastructure. Part three is the reference grammar.

The protocol is specified in four open Internet-Drafts at the IETF (the standards body for internet protocols), so anyone can implement it. These are early-stage submissions, not yet ratified standards, and each section links the draft it draws from.

How to read the badges:Liveworks against ~alter’s production endpoints now.Specimplementable from the draft on your own infrastructure.Rolling outspecified and shipping; not fully hosted yet.

What a ~handle is

A ~handle is the address through which one Alter resolves another. Where @ answers where can I reach you, ~ answers who are you, verifiably.

A handle is a protocol-level identifier consumed by agents, Org Alters (the identity record an organisation publishes for its domain), and applications. Most users never type it. The tilde is infrastructure, not UI.

Part 1: Build with ~alter today

Everything in this part is Live and verifiable from your terminal right now. You are a consumer of the protocol here: you read records ~alter already publishes.

Resolve ~alter over DNS Live

~alter’s identity entry point is published as DNS TXT records (plain text records attached to a domain name). Two records carry the bootstrap: _alter.<domain> advertises the organisation’s identity card, and _mcp.<domain> advertises its MCP endpoint. No ~alter account is required to read them.

# Resolve over DNS-over-HTTPS (works without dig installed).
curl -s 'https://dns.google/resolve?name=_alter.truealter.com&type=TXT'

# The TXT answer (published today):
# v=alter1; mcp=https://mcp.truealter.com; pk=ed25519:1s9Ml54Jjx0j0KNg5BZ6IQ3YTzvzVhUMF-kvy5Qo53A; epoch=1; x402=base:0x8c78c8a2c12f18001170530650ccab4b76dc1e13; cap=E4; attest=employ,contract,member,contrib; org=true-alter

The pk field is an Ed25519 public key (a compact, fast signature key). The epoch counts key rotations. Drawn from draft-morrison-mcp-dns-discovery. The x402= field names the settlement contract; on-chain settlement is not yet live.

Connect the MCP endpoint Live

The _mcp record points at an MCP endpoint. MCP (the Model Context Protocol) is the open JSON-RPC interface AI agents use to call tools. ~alter serves it over Streamable HTTP at the advertised url. Connect, list the identity tools, and call one.

typescript
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';

// The endpoint advertised by the _mcp.truealter.com record.
const transport = new StreamableHTTPClientTransport(
  new URL('https://mcp.truealter.com/api/v1/mcp'),
);
const client = new Client({ name: 'my-agent', version: '1.0.0' });
await client.connect(transport);

// Discover the identity tools, then verify a handle.
const { tools } = await client.listTools();
const result = await client.callTool({
  name: 'verify_identity',
  arguments: { target: '~blake' },
});

The full tool catalogue, free and paid, is on the MCP tools reference. Anonymous reads are free; deeper identity queries are gated by consent and metered by x402 payment.

Pronouns resolve before the wire

A client may hold a session pronoun such as ~org that stands in for the current organisation. Pronouns are resolved to a concrete handle on the client, before any DNS lookup, signature, or wire payload. A pronoun never crosses a trust boundary unresolved. See draft-morrison-identity-pronouns.

Read the discovery card over HTTPS Live

The same identity card is served over HTTPS at /.well-known/alter.json, for clients that prefer a fetch to a DNS lookup. This is the organisation’s discovery card: its endpoint and signing key. It is the entry point, not a signed per-person record (that is the envelope, in Part 2).

typescript
const res = await fetch('https://truealter.com/.well-known/alter.json');
const card = await res.json();

// card.mcp   -> the MCP endpoint, e.g. "https://mcp.truealter.com"
// card.pk    -> the org signing key, "ed25519:..."
// card.epoch -> the key-rotation counter
// card.org   -> "true-alter"

The HTTPS card and the DNS record are two surfaces for the same identity entry point; either is sufficient to bootstrap a connection.

Attribute your own commits Live

The most direct way to apply the protocol in your own workflow needs no ~alter infrastructure at all: identity-attributed commit trailers. You add lines to a commit’s footer block that bind the work to a ~handle. Three tiers, three trailers: Acted-By for the person who authored it, Executed-By for a bot, and Drafted-With for the AI instrument that helped.

bash
Refactor the resolver cache

Body explaining what changed and why.

Acted-By: ~you
Drafted-With: ~cc-opus-4.8

When signed, the signature covers the commit’s tree hash rather than its commit hash, so attribution survives rebase, cherry-pick, and squash-merge. The canonical trailer order is:

  1. 1Acted-By

    The sovereign author. Exactly one, or several on a squash-merge.

  2. 2Executed-By

    The bot, if any. At most one.

  3. 3Drafted-With

    The AI instruments. Zero or more.

  4. 4Identity-Signature + Identity-Key-Id

    Together or not at all.

A verifier resolves each handle’s tier from its _alter record and rejects a handle placed in the wrong slot. Full grammar in draft-morrison-identity-attributed-commits.

Part 2: Implement the open protocol

This part is for building on your own infrastructure: publishing your own identity, signing your own records, and dispatching handle references through the operating system. You become a publisher of the protocol, not just a consumer.

Publish your own discovery record Spec

You can publish an identity entry point for your own domain today: it is a single DNS TXT record in the same format ~alter publishes. Generate an Ed25519 key, then add the record at _alter.<yourdomain>.

bash
# 1. Generate an Ed25519 keypair (OpenSSL shown; any Ed25519 tool works).
openssl genpkey -algorithm ed25519 -out alter.key
openssl pkey -in alter.key -pubout -outform DER | tail -c 32 | basenc --base64url

# 2. Add the TXT record at your DNS provider:
_alter.example.com.  300  IN  TXT  "v=alter1; mcp=https://mcp.example.com; pk=ed25519:<your-base64url-key>; epoch=1; org=example"

# 3. Confirm it resolves.
curl -s 'https://dns.google/resolve?name=_alter.example.com&type=TXT'

Sign your zone with DNSSEC (the DNS security extension that lets resolvers verify a record was not tampered with) so consumers can trust the record end-to-end. Record format from draft-morrison-mcp-dns-discovery.

Sign a per-handle envelope Rolling out

The discovery record above is organisation-level. The protocol’s per-person primitive is the envelope: a signed JSON object binding one handle to its key, an inception time, a transparency-log root, and a revocation commitment. ~alter’s live records are organisation-level today; per-handle signed envelopes are specified and rolling out, so build against the draft rather than against a live ~alter example.

json
{
  "handle": "~you",
  "pubkey": "ed25519:<base64url-32-byte-key>",
  "inception_ts": 1729123456,
  "identitylog_root": "<base64url sha-256>",
  "revocation_hash": "<base64url sha-256>",
  "signature_alg": "Ed25519",
  "signature": "<base64url>",
  "caveats": []
}

To verify one: take the envelope without its signature field, canonicalise it with JCS (RFC 8785, a deterministic JSON serialisation: keys sorted, no whitespace), then check the Ed25519 signature over those exact bytes using pubkey. The DNS key ordering is a convenience; the signed bytes are always the JCS of the JSON.

Envelope schema and the eleven-step verification from draft-morrison-mcp-dns-discovery.

Emit and dispatch alter: URIs Spec

The alter: URI scheme makes a handle clickable. Emit one in a document, a chat client, or terminal output; a registered handler resolves the envelope and dispatches to the right application. The grammar (ABNF, the notation from RFC 5234):

abnf
alter-URI    = "alter:" handle-ref [ "/" handle-path ] [ "?" query ] [ "#" fragment ]
handle-ref   = "~" handle-name
handle-name  = sovereign-name / bot-name / instrument-name
sovereign-name   = ALPHA *( ALPHA / DIGIT / "-" / "." )
bot-name     = ALPHA *( ALPHA / DIGIT / "-" / "." ) ".bot"
instrument-name  = "cc-" 1*( ALPHA / DIGIT / "-" / "." )
handle-path  = segment *( "/" segment )

Note the registers differ: a sovereign-name in an alter: URI permits dots (for domain-qualified names such as ~example.com), while a bare handle in a tilde-ref does not.

Examples:

bash
alter:~alice
alter:~example.com/decisions/123
alter:~bob/inbox
alter:~cc-example-model/sessions/last

Register a handler on Linux with a desktop entry:

bash
# ~/.local/share/applications/alter-handler.desktop
[Desktop Entry]
Type=Application
Name=Alter Handle Resolver
Exec=/usr/bin/alter-open %u
MimeType=x-scheme-handler/alter;

macOS uses a CFBundleURLSchemes entry; Windows a registry key. Provisional registration per RFC 7595, specified in draft-morrison-alter-uri-scheme.

Bridge into the fediverse Spec

Any ActivityPub actor can advertise an ~alter envelope through WebFinger (RFC 7033, the protocol Mastodon and friends use to resolve @user@host). A sidecar shim adds one link relation to the actor’s WebFinger response, pointing at the envelope, and leaves everything else untouched:

json
{
  "rel": "https://truealter.com/rel/envelope",
  "type": "application/alter-envelope+json",
  "href": "https://example.com/.well-known/alter/alice.json"
}

The instance keeps its identity; the link is additive. No envelope is created, only advertised.

Part 3: Protocol reference

The grammar, composition forms, resolution paths, and response shapes, for implementers who need the exact surface.

Handle grammar

Handle syntax is defined by the following ABNF grammar (RFC 5234):

abnf
tilde-ref   = "~" handle [ "@" org ] [ ":" facet ] [ "/" action ]
handle      = 1*63 handle-char
handle-char = ALPHA / DIGIT / "-" / "_"   ; [a-z0-9_-], lowercase
org         = domain-name                 ; per RFC 1035 section 2.3.1
facet       = ALPHA *( ALPHA / DIGIT / "-" )
action      = ALPHA *( ALPHA / DIGIT / "-" / "/" )
  • Handles are case-insensitive and stored lowercase.
  • Maximum length is 63 characters (the DNS label limit).
  • Handles MUST NOT begin or end with a hyphen or underscore.
  • Handles MUST contain at least one alphabetic character (no purely-numeric handles).
  • The @org qualifier is a domain name and MUST resolve to a domain with a valid _alter. DNS record (or .well-known/alter.json fallback).

Composition

ExpressionMeaning
~darrylBare handle: resolved in current context.
~darryl@bonniedoon.comDomain-qualified: resolved through _alter.bonniedoon.com.
~darryl:securityFaceted: request the security facet of darryl’s identity.
~darryl/verifyActioned: invoke the verify action on darryl’s identity.
~darryl@bonniedoon.com:security/verifyFull composition: all components present.

Three resolution paths

The structure of the handle determines the resolution path. The full flowcharts live in the normative spec; the headlines:

  1. 1Domain-qualified (~handle@domain)

    The most explicit form. DNS TXT lookup of _alter.<domain>, MCP handshake with the discovered endpoint, resolve_handle call, signed response. The domain qualifier encodes the resolution path directly.

  2. 2Global reserved (~handle, registered)

    For globally-unique handles registered at L3 engagement. Query the ~alter global registry at mcp.truealter.com, redirect to the home Org Alter, resolve. Affiliated Org Alters are available for federated queries.

  3. 3Bare handle (~handle, no context)

    Ambiguous by design. If the agent is in an active Org Alter session, resolve within that Org Alter. Otherwise, fall back to mcp.truealter.com. If multiple matches exist, return 300 Multiple Choices and let the agent or user disambiguate.

Identity card schema

A successful resolution returns a signed identity card. Agents MUST reject responses older than five minutes and MUST verify the signature against the Org Alter’s key from DNS.

json
{
  "v": "1",
  "handle": "~darryl",
  "domain": "truealter.com",
  "engagement_level": "L3",
  "public_key": "ed25519:BASE64URL_INDIVIDUAL_PUBKEY",
  "data": {
    "traits": { },
    "attestations_summary": { },
    "belonging_signals": { }
  },
  "attestations": [
    {
      "attester": "truealter.com",
      "type": "org_attestation",
      "issued": "2026-04-07T00:00:00Z",
      "signature": "ed25519:BASE64URL_ORG_SIGNATURE"
    }
  ],
  "signature": "ed25519:BASE64URL_RESPONSE_SIGNATURE",
  "timestamp": "2026-04-07T00:00:00Z",
  "nonce": "BASE64URL_RANDOM_NONCE"
}

The engagement_level (here L3, the Augmented level at which a member’s Alter activates and can earn) gates what data the card carries. See the consent model.

Latency budget

Identity resolution must complete within strict bounds to remain viable for agent-to-agent traffic. Row budgets are cold-path; the warm path skips the DNS TXT and registry phases, which is why its total is lower than the row sum.

PhaseTarget
DNS TXT lookup< 5 ms
Global registry lookup< 10 ms
MCP handshake< 50 ms
Consent evaluation + response< 30 ms
Warm path total< 50 ms
Cold path total< 100 ms

Scope at launch

The handle namespace at first launch is closed beta: founding handles, reserved system handles, and an invite-only cohort. The namespace will open as soon as the protocol is ready to carry it. There is no public registration endpoint and no waitlist yet.

Implementations of this spec are encouraged. Adoption of the handle namespace at scale is intentionally gated separately from adoption of the protocol.

See also

  • MCP Identity Server: protocol overview, transport, and authentication.
  • x402 Payments: how metered identity queries pay the data subject.
  • Consent Model: engagement levels and the gates on every query.
  • Developers: install paths, discovery surfaces, and the standards index.
Docs | ~alter