Skip to main content

The did:permara DID method

Version: 1.0.0 (draft) · Date: 2026-09-13 · Maintainer: Permara

Abstract

did:permara identifies a business or person that can be paid through Permara. The identifier is generated, not chosen, and is never derived from a name, a key or a chain. Its state lives in a small registry contract on an EVM network; its DID document lives at an HTTPS location the registry names and is accepted only when the document's canonical hash equals the hash the registry holds. This document specifies the method as required by the W3C Decentralized Identifiers (DIDs) v1.0 Recommendation and the method requirements of DIDs v1.1.

Status of this document

Draft of the first method version. It conforms to DID Core v1.0 and is written to satisfy the method requirements of DID v1.1. Nothing here is endorsed by the W3C; registration in the DID Extensions repository is a discoverability step.

Conformance and terminology

The key words MUST, MUST NOT, SHOULD and MAY are to be interpreted as described in RFC 2119. DID, DID document, DID controller, DID subject, verification method, verification relationship, service and verifiable data registry have their DID Core meanings. "Registry" means the PermaraDidRegistry contract named in the registry table. "Operator" means Permara, the party that holds the registrar role.

Method name

The method name is permara. A DID that uses this method MUST begin with did:permara:.

Method-specific identifier

Syntax

permara-did = "did:permara:" suffix
suffix = 64lowerhex
lowerhex = DIGIT / %x61-66 ; 0-9 a-f

Example:

did:permara:7f5df4d16e3c8a2e4b1c9d0a3f6e8b7c2d5a4f1e9c8b7a6d5e4f3c2b1a0f9e8d

Generation

The suffix is the lowercase hexadecimal encoding of 32 bytes drawn from a cryptographically secure random source. The suffix is used directly as the registry's 32-byte record key. No hashing, encoding prefix or checksum is applied.

Normalization

There is no normalization. A resolver MUST reject any input whose suffix is not exactly 64 characters from 0-9a-f, including uppercase hexadecimal, a 0x prefix, or any additional path segment. DIDs are compared byte for byte after trimming surrounding whitespace at the transport boundary.

Uniqueness

Global uniqueness follows from the random source. The registry rejects a create for a suffix that already has a record, and the operator's database enforces uniqueness on the suffix, so a collision cannot produce two records for one DID.

Networks

The identifier carries no network segment. Which network and contract hold the records is a property of the method version, published in the registry table below. A DID minted while the registry lived on one network keeps resolving after the operator publishes a new registry on another network, because the operator re-creates every live record there before retiring the old one.

Data model

Registry record

Each record is keyed by the suffix and holds:

FieldTypeMeaning
controlleraddressMay publish a new document version and hand control on.
recoveryaddressMay replace both authorities and may deactivate.
documentHashbytes32sha256 over the canonical DID document.
documentUristringWhere the document is served. 1 to 256 bytes.
versionuint32Document version. 1 at create, incremented by each update.
createdAt, updatedAtuint40Block timestamps.
deactivatedboolAbsorbing.

DID document

A resolved document MUST contain id equal to the DID. It SHOULD contain the verification methods that prove control on behalf of the subject, the verification relationships that use them, an alsoKnownAs entry for the subject's credential identifier where one exists, and a single service of type PermaraPaymentProfile whose endpoint is an authenticated API. The document MUST NOT contain names, email addresses, phone numbers, payment endpoints or any other personal data. Verification methods are of type EcdsaSecp256k1RecoveryMethod2020 with a CAIP-10 blockchainAccountId. Fragment identifiers are #signer-<n> in ascending address order so the canonical form is deterministic.

{
"@context": ["https://www.w3.org/ns/did/v1"],
"id": "did:permara:7f5df4d16e3c8a2e4b1c9d0a3f6e8b7c2d5a4f1e9c8b7a6d5e4f3c2b1a0f9e8d",
"verificationMethod": [
{
"id": "did:permara:7f5d…9e8d#signer-1",
"type": "EcdsaSecp256k1RecoveryMethod2020",
"controller": "did:permara:7f5d…9e8d",
"blockchainAccountId": "eip155:1:0x1111111111111111111111111111111111111111"
}
],
"authentication": ["did:permara:7f5d…9e8d#signer-1"],
"capabilityInvocation": ["did:permara:7f5d…9e8d#signer-1"],
"service": [
{
"id": "did:permara:7f5d…9e8d#payment-profile",
"type": "PermaraPaymentProfile",
"serviceEndpoint": "https://api.permara.com/v1/payment-profile"
}
]
}

Canonicalization and hash

documentHash is sha256 over the UTF-8 bytes of the document serialized with the JSON Canonicalization Scheme (RFC 8785). Two conforming implementations serializing the same document MUST produce the same bytes and therefore the same hash.

Operations

Create

  1. The operator generates the suffix and builds the initial document.
  2. The operator serves the document at documentUri and computes documentHash.
  3. An address holding the registrar role calls create(suffix, documentHash, documentUri, controller, recovery).
  4. The record is final once the transaction is included and the network's finality rule (below) is met.

The registry reverts on a zero suffix, a zero hash, a zero address, an empty or over-long URI, and a suffix that already has a record.

Resolve

Input: a DID and resolution options. Output: a DID resolution result with didResolutionMetadata, didDocument and didDocumentMetadata.

  1. Validate syntax. Failure: didResolutionMetadata.error = "invalidDid". A different method: "methodNotSupported".
  2. Read getRecord(suffix) from the current registry. No record (createdAt == 0): "notFound".
  3. If deactivated: return didDocument containing only id, and didDocumentMetadata.deactivated = true with versionId, created and updated.
  4. Fetch the document from documentUri with bounded size and time.
  5. Verify document.id equals the requested DID and sha256(JCS(document)) equals documentHash. Failure: "internalError"; a resolver MUST NOT return a document that failed either check.
  6. Return the document with didDocumentMetadata.created, updated, versionId (<chainId>:<transaction hash>:<version>) and deactivated = false.

A conforming resolver only needs a JSON-RPC endpoint for the registry's network and an HTTPS client; it does not need to trust the operator's API for the result to be authentic.

Update

The controller publishes a new document version by calling update(suffix, documentHash, documentUri). The registry increments version. Until the update is final, the previously anchored document remains the served document so that bytes and hash always agree.

Rotate controller

The controller calls rotateController(suffix, newController). The document version is unchanged.

Recover

The recovery authority calls recover(suffix, newController, newRecovery). This works while the registry is paused so that an incident can never lock out recovery. The previous controller and the previous recovery authority lose all rights.

Deactivate

The controller or the recovery authority calls deactivate(suffix). Deactivation is absorbing: every later write to the record reverts, the suffix can never be created again, and resolution reports deactivated: true. This also works while paused.

DID URL dereferencing

Only fragment identifiers are supported in this version. Dereferencing did:permara:<suffix>#<fragment> resolves the DID and returns the verification method or service whose id matches. Paths and query parameters are not defined and MUST be rejected.

Versioning and finality

versionId is the registry's version qualified by the chain id and the transaction hash that set it. Historical versions are not resolvable in this method version. A record change is final once the transaction is included and the confirmation depth in the registry table is reached; resolvers SHOULD NOT treat an unconfirmed change and a final one as equivalent. After a chain reorganization the operator reconciles its mirror from the registry, never the other way round.

Authorization

This method version is operator-registered. Only addresses holding the registrar role may create records; the operator holds controller and recovery for every record it creates. Subjects hold the verification keys listed in their documents and use them to authorize actions in Permara; those keys never write to the registry. rotateController exists so a subject can take control of its own record in a later method version.

Cryptographic algorithms

  • Random source: 256 bits from a cryptographically secure generator.
  • Hash: SHA-256 over RFC 8785 canonical JSON.
  • Verification method type: EcdsaSecp256k1RecoveryMethod2020 (secp256k1, CAIP-10 account identifiers).
  • Registry authorization: EVM transaction signatures (secp256k1).

Registry table

Method versionNetworkChain idContractConfirmations
1.0Base Sepolia84532published in contracts/deployments/84532.json after deployment1
1.0Base8453not yet deployed2

The contract is not upgradeable. A new contract is a new row and a new method minor version; the operator re-creates live records on the new contract before retiring the old row.

Security considerations

  • Registrar key compromise. An attacker can create records and, as controller, publish documents for them. The recovery authority, held separately on a multi-signature account, can recover every affected record and the operator can pause creates. Recovery and deactivation work while paused.
  • Recovery key compromise. The attacker can replace authorities on any record. Mitigation is the multi-signature threshold and the pause; the contract has no admin key that can rewrite records, so the blast radius is the records the attacker touches, each of which emits an event.
  • Replay. Registry writes are EVM transactions bound to chain id, nonce and contract; a transaction for one network cannot be replayed on another.
  • Document substitution. A document served from documentUri that does not hash to documentHash MUST be rejected. A resolver that trusts the operator's HTTPS endpoint without checking the hash is not a conforming resolver.
  • Storage disappearance. The registry stores the hash and the location, not the document. The operator keeps every anchored version and serves the last anchored one; a resolver may cache documents keyed by hash.
  • Reorganizations and equivocating RPC providers. Resolvers SHOULD read from more than one provider or wait for the table's confirmation depth before treating a change as final.
  • Denial of service. documentUri is bounded to 256 bytes on chain. Resolvers MUST bound document size, fetch time, redirects and decompression.
  • Man in the middle on resolution. Resolution output is authenticated by the hash check, not by transport; TLS protects confidentiality of what is being resolved.

Privacy considerations

  • No personal data is written to the registry or into any document: no names, contact details or payment endpoints. The service endpoint is an authenticated API.
  • A DID is a stable identifier and can be correlated across payments. The method does not offer pairwise DIDs in this version; subjects who need relationship separation must use separate accounts.
  • Verification methods expose signer addresses. Those addresses are already public on the networks they transact on; the document adds a link between an address and a DID, which is the purpose of the method.
  • Records created on a test network carry no production assurance and are published in the registry table as such.

Governance

Permara, as operator, publishes changes to the registry table, the confirmation depths, the supported cryptographic suites and the resolver behaviour through new versions of this document, with a sunset notice of at least ninety days before any row of the registry table is retired. The contract has no upgrade path; changes to contract behaviour are new deployments.

Test vectors

Valid:

did:permara:0000000000000000000000000000000000000000000000000000000000000001
did:permara:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

Invalid, with the expected resolution error:

InputError
did:permara:FFFF… (uppercase)invalidDid
did:permara:0xff… (prefix)invalidDid
did:permara:abc (wrong length)invalidDid
did:permara:<suffix>/pathinvalidDid
did:example:1234methodNotSupported
syntactically valid, no recordnotFound
record with deactivated = truedidDocumentMetadata.deactivated = true

Canonicalization: the document {"b":1,"a":[2,{"d":true,"c":"x"}]} canonicalizes to {"a":[2,{"c":"x","d":true}],"b":1} and its SHA-256 is the hash a registry would hold for it.