Skip to content

Latest commit

 

History

History
300 lines (207 loc) · 7.03 KB

File metadata and controls

300 lines (207 loc) · 7.03 KB

TFEP Domain Registry — REST API Reference

Base URL: http(s)://<gateway-host>/api/v1/registry

All endpoints that modify state require Bearer token auth (Authorization: Bearer <key>) unless noted otherwise. Read endpoints are public and require no authentication.


Name Registration

POST /api/v1/registry/register

Register a new .tpt name. Each name is registered once and never renewed.

Authentication: Required (Bearer token)

Request body:

{
  "name":         "alice",
  "did":          "did:web:alice.tpt",
  "owner_pubkey": "abc123...",
  "fee_tx":       "optional-payment-proof"
}
Field Type Required Description
name string yes Desired name without the .tpt suffix. Must match ^[a-z0-9][a-z0-9-]{0,62}$.
did string yes DID to associate with this name, typically did:web:<name>.tpt.
owner_pubkey string yes Hex-encoded Ed25519 public key of the registrant. Used to verify future transfer signatures.
fee_tx string no Payment proof token (provider-specific). Required when the gateway has fee validation enabled.

Responses:

Status Meaning
201 Created Registration successful. Returns the DomainRecord JSON.
400 Bad Request Invalid name format or missing required fields.
402 Payment Required Fee validation failed (gateway has payment enforcement enabled).
409 Conflict Name already registered.
503 Service Unavailable Registry not enabled on this gateway.

Example response (201):

{
  "name": "alice",
  "did": "did:web:alice.tpt",
  "owner_pubkey": "abc123...",
  "registered_at": "2026-05-31T12:00:00Z",
  "dormant": false
}

Name Resolution

GET /api/v1/registry/resolve/{name}

Resolve a .tpt name to its DID and metadata. Public — no authentication required.

Path parameter: name — the name without .tpt suffix (e.g. alice).

Responses:

Status Meaning
200 OK Name found. Returns DomainRecord JSON.
404 Not Found Name not registered.

Example:

GET /api/v1/registry/resolve/alice
{
  "name": "alice",
  "did": "did:web:alice.tpt",
  "owner_pubkey": "abc123...",
  "registered_at": "2026-05-31T12:00:00Z",
  "dormant": false,
  "metadata": { "bio": "Hi, I'm Alice." }
}

dormant: true indicates the name is registered but the owner has not yet published a DID document at https://<name>.tpt/.well-known/did.json.


Reverse Lookup

GET /api/v1/registry/lookup?did=<did>

Find all .tpt names owned by a given DID. Public — no authentication required.

Query parameter: did — the full DID string (e.g. did:web:alice.tpt).

Response: Array of DomainRecord objects (may be empty).

GET /api/v1/registry/lookup?did=did%3Aweb%3Aalice.tpt
[
  { "name": "alice", "did": "did:web:alice.tpt", ... }
]

Name Search

GET /api/v1/registry/search?q=<query>

Fuzzy search for registered names. Returns up to 50 results. Public — no authentication required.

Query parameter: q — partial name string to search for.

Response: Array of DomainRecord objects.

GET /api/v1/registry/search?q=ali

Ownership Transfer

POST /api/v1/registry/transfer

Transfer ownership of a name to a new DID + keypair. The current owner must sign the transfer payload with their Ed25519 private key.

Authentication: Required (Bearer token)

Signature payload — sign the following string with the current owner's Ed25519 key:

transfer:<name>:<new_did>:<new_owner_pubkey>:<timestamp_unix>

Request body:

{
  "name":             "alice",
  "new_did":          "did:web:alice-v2.tpt",
  "new_owner_pubkey": "def456...",
  "signature":        "<hex-encoded Ed25519 signature>",
  "timestamp":        1748692800
}

Responses:

Status Meaning
200 OK Transfer successful.
403 Forbidden Signature verification failed.
404 Not Found Name not registered.

Transfer History

GET /api/v1/registry/transfer/{name}/history

Retrieve the full ownership change log for a name. Public — no authentication required.

Response: Array of Transfer objects, ordered oldest-first.

[
  {
    "id": 1,
    "name": "alice",
    "from_did": "did:web:alice.tpt",
    "to_did": "did:web:alice-v2.tpt",
    "to_owner_pubkey": "def456...",
    "transferred_at": "2026-06-15T09:00:00Z",
    "signature": "abcdef..."
  }
]

Metadata Update

PUT /api/v1/registry/metadata/{name}

Update the JSON metadata blob for a registered name. The current owner must sign the update.

Authentication: Required (Bearer token)

Signature payload — sign the following string with the current owner's Ed25519 key:

metadata:<name>:<sha256-hex-of-metadata-json>

Request body:

{
  "metadata":  { "bio": "Hi, I'm Alice.", "url": "https://alice.example.com" },
  "signature": "<hex-encoded Ed25519 signature>"
}

Responses:

Status Meaning
200 OK Metadata updated.
403 Forbidden Signature verification failed.
404 Not Found Name not registered.

DID Document (well-known)

GET /.well-known/did.json

Returns the gateway's own DID document, including any .tpt names owned by the gateway DID in the alsoKnownAs array. Public.

Content-Type: application/did+json


Public Profile Pages

GET /p/{name}

Serves an HTML profile page for a registered .tpt name, showing identity, DID, pubkey, metadata, and transfer history. Suitable for linking in email signatures or social profiles.

Example: http://localhost:8080/p/alice


Data Schemas

DomainRecord

{
  name:          string       // name without .tpt
  did:           string       // associated DID
  owner_pubkey:  string       // hex Ed25519 public key
  registered_at: string       // RFC 3339 timestamp
  fee_tx?:       string       // payment proof (if any)
  metadata?:     object       // arbitrary JSON metadata
  dormant?:      boolean      // true = no DID doc published yet
}

Transfer

{
  id:             number       // sequential ID
  name:           string
  from_did:       string
  to_did:         string
  to_owner_pubkey: string
  transferred_at: string       // RFC 3339 timestamp
  signature:      string       // hex Ed25519 signature by previous owner
}

Error Format

All error responses use the same JSON envelope:

{ "error": "human-readable message" }

Fee Validation

By default, the gateway uses a no-op fee validator that accepts any fee_tx value (including empty string). To require payment:

  1. Implement the api.FeeValidator interface in your deployment:
    type FeeValidator interface {
        ValidateFee(ctx context.Context, feeTx string) error
    }
  2. Set api.Config.FeeValidator before starting the server.

Common implementations: Stripe payment intent verification, Lightning invoice preimage check, or on-chain transaction confirmation.