Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Registry

This document specifies the Registry application profile: a special enclave that maps enclave IDs to the node endpoints hosting them, resolves identity public keys to their published enclaves, and provides context lookup for what each enclave represents. The Registry is itself an ENC enclave plus a DataView surface (see node-api.md §Registry DataView API).



Table of Contents

  1. Registry Purpose
  2. Manifest
  3. State Transitions
  4. Event-Operator Matrix
  5. Content Events
  6. Confidentiality
  7. Registry Operations
  8. DataView API

Registry Purpose

The Registry is a special enclave that maps Enclave IDs to the Node endpoints that host them, and can optionally attach descriptive metadata.

Purpose:
  • Service discovery: resolve enclave_id → node(s)
  • Identity resolution: resolve id_pub → enclave(s)
  • Context lookup: understand what an enclave represents
Registry Record:

A registry entry MAY include:

  • enclave_id — canonical enclave identifier
  • nodes — hosting node endpoints or identifiers
  • app (optional) — application that created or uses the enclave
  • creator (optional) — identity key that initialized the enclave
  • desc (optional) — human-readable description
  • meta (optional) — application-defined metadata
Trust Model:
  • Sequencer discovery: When clients choose to use the Registry enclave for discovery, the Registry is authoritative for the sequencer mapping. Clients SHOULD re-check Registry periodically to catch migrations. Discovery via other mechanisms (DNS, well-known URLs, bootstrap configuration) is equally valid and out of scope for this document.
  • Enclave metadata: Registry is advisory. Clients SHOULD verify enclave proofs independently.

Manifest

The Registry uses a minimal RBAC v2 manifest. No States (stateless enclave), two traits: owner and dataview.

{
  "enc_v": 2,
  "RBAC": {
  "states": [],
  "traits": ["owner(0)", "dataview(1)"],
  "readers": [
    { "type": "Public", "reads": "*" }
  ],
  "grants": [
    { "event": "Grant", "operator": ["owner"], "scope": ["OUTSIDER"], "trait": ["dataview"] },
    { "event": "Revoke", "operator": ["owner"], "scope": ["OUTSIDER"], "trait": ["dataview"] }
  ],
  "transfers": [
    { "trait": "owner", "scope": ["OUTSIDER"] }
  ],
  "slots": [],
  "lifecycle": [
    { "event": "Terminate", "operator": "owner", "ops": ["C"] }
  ],
  "customs": [
    { "event": "reg_node", "operator": "Public", "ops": ["C"] },
    { "event": "reg_node", "operator": "Sender", "ops": ["U", "D"] },
    { "event": "reg_node", "operator": "dataview", "ops": ["P"] },
    { "event": "reg_enclave", "operator": "Public", "ops": ["C"] },
    { "event": "reg_enclave", "operator": "Sender", "ops": ["U", "D"] },
    { "event": "reg_enclave", "operator": "dataview", "ops": ["P"] },
    { "event": "reg_identity", "operator": "Public", "ops": ["C"] },
    { "event": "reg_identity", "operator": "Sender", "ops": ["U", "D"] },
    { "event": "reg_identity", "operator": "dataview", "ops": ["P"] }
  ],
  "init": [
    { "identity": "<registry_owner_id>", "state": "OUTSIDER", "traits": ["owner"] }
  ]
}
Authorization rules:
  • Public can read all events (* wildcard R via readers)
  • dataview trait receives all events via Push (P) to serve the REST API
  • Public can submit reg_node, reg_enclave, reg_identity (signed by submitter)
  • Only the original sender can Update or Delete their own entries (Sender context)
  • Sender for reg_node is evaluated against content.seq_pub.
  • Sender for reg_enclave is evaluated against content.manifest_event.from.
  • Sender for reg_identity is evaluated against content.id_pub.
  • owner trait can Grant/Revoke the dataview trait (manage push endpoints)
  • owner trait can Transfer ownership and Terminate
Strict Self-Authorization:

For reg_node: the commit's from field MUST equal content.seq_pub. One identity cannot register another node.

For reg_enclave: the commit's from field MUST equal content.manifest_event.from. One identity cannot register another's enclave.

For reg_identity: the commit's from field MUST equal content.id_pub. One identity cannot register enclaves for another. The verifier MUST accept either of two valid signature paths:

  1. Direct. commit.sig is the parent identity's own signature over commit.hash, dispatched per commit.alg (Schnorr or ECDSA — see §Signature Schemes).
  2. Sub-key via cosign cert. commit.cert carries a valid Delegated Sub-keys cert, commit.cert.parentId == commit.from == content.id_pub, and commit.sig is the sub-key's Schnorr signature over commit.hash. Cert-authorized reg_identity is the path used by MetaMask-style ECDSA-only wallets that publish their sub_pub through their own reg_identity entry.

Both paths satisfy the Sender check on reg_identity: Sender is evaluated against content.id_pub (= commit.from), not against commit.cert.subkey. RBAC and discovery remain keyed to the parent identity regardless of which signature scheme produced the commit.

reg_enclave and Ownership Transfer:

The Registry tracks the original creator (manifest_event.from) as the default authorized identity. However, after Transfer, the new Owner can update the Registry by providing an owner_proof.

Update Scenarios:
Scenariofrom fieldowner_proof
Original creator updatesmanifest_event.fromNot required
New Owner updates (no migration)New Owner's keyRequired (SMT proof of owner trait)
New Owner updates (after migration)New Owner's keyRequired (SMT proof + migrate_event)

See the Authorization section in reg_enclave for verification details.

State Transitions

Registry is stateless: the manifest declares an empty states array. No state-transition (Move) events are defined or accepted. Authorization is trait-based (owner, dataview) and per-event Sender self-checks, not state-based.

Event-Operator Matrix

Derived from the manifest above:

EventOperatorOpsNotes
GrantownerCIssues dataview trait to a node serving the REST API
RevokeownerCRevokes dataview trait
TransferownerCTransfers owner trait to a new identity
TerminateownerCLifecycle: closes the Registry enclave
reg_nodePublicCAnyone may submit; signed by seq_pub
reg_nodeSenderU, DOnly the original submitter (from == content.seq_pub)
reg_nodedataviewPPushed to the DataView server
reg_enclavePublicCAnyone may submit; signed by enclave creator or current owner
reg_enclaveSenderU, DOnly the original submitter (from == content.manifest_event.from)
reg_enclavedataviewPPushed to the DataView server
reg_identityPublicCAnyone may submit; signed by id_pub (parent or sub-key path)
reg_identitySenderU, DOnly the original submitter (from == content.id_pub)
reg_identitydataviewPPushed to the DataView server
ReadPublic (reads: "*")RAll events are public; no encryption

Content Events

reg_node, reg_enclave, and reg_identity are Content Events within the Registry enclave. They can be Updated (to change metadata) or Deleted (to deregister) per the Registry's RBAC schema. Each follows the same lifecycle (Public:C, Sender:U/D, dataview:P) but carries different content.

reg_node

Type: reg_node

Purpose: Register a node in the Registry for discovery.

Content Structure

{
  "seq_pub": "<node's sequencer public key>",
  "endpoints": [
    {
      "uri": "https://node.example.com:443",
      "priority": 1
    },
    {
      "uri": "https://1.2.3.4:443",
      "priority": 2
    }
  ],
  "protocols": ["https", "wss"],
  "enc_v": 2
}

Fields

FieldRequiredDescription
seq_pubYesNode's sequencer public key
endpointsYesArray of endpoints, ordered by priority (lower = preferred)
endpoints[].uriYesFull URI including protocol and port
endpoints[].priorityNoResolution order (default: array index)
protocolsNoSupported transport protocols
enc_vYesENC protocol version

Validation Limits

FieldMax
endpoints array10 entries
endpoints[].uri2048 characters
protocols array10 entries

Nodes MUST reject reg_node commits exceeding these limits.

Authorization

  • The commit MUST be signed by seq_pub (proves ownership).
  • from field MUST equal seq_pub.

Update/Deregister

  • To update: submit new reg_node (replaces previous)
  • To deregister: submit Delete event referencing the reg_node event

reg_enclave

Type: reg_enclave

Purpose: Register an enclave in the Registry for discovery.

Content Structure

{
  "manifest_event": {
    "id": "<event_id>",
    "hash": "<commit_hash>",
    "enclave": "<enclave_id>",
    "from": "<creator_id_pub>",
    "type": "Manifest",
    "content": "...",
    "exp": 1706000000000,
    "tags": [],
    "timestamp": 1706000000000,
    "sequencer": "<sequencer_id_pub>",
    "seq": 0,
    "sig": "<creator_signature>",
    "seq_sig": "<sequencer_signature>"
  },
  "owner_proof": {
    "sth": {
      "t": 1706000000000,
      "ts": 500,
      "r": "<ct_root_hex>",
      "sig": "<sth_signature_hex>"
    },
    "ct_proof": {
      "ts": 500,
      "li": 499,
      "p": ["<hex64>", ...]
    },
    "state_hash": "<hex64>",
    "events_root": "<hex64>",
    "smt_proof": {
      "k": "<hex42>",
      "v": "<hex64>",
      "b": "<hex42>",
      "s": ["<hex64>", ...]
    }
  },
  "app": "my-chat-app",
  "desc": "A group chat for project X",
  "meta": {}
}

Fields

FieldRequiredDescription
manifest_eventYesThe finalized Manifest event (full event structure)
owner_proofNoProof of current Owner status (required if frommanifest_event.from)
appNoApplication identifier
descNoHuman-readable description
metaNoApplication-defined metadata

Owner Proof Structure

The owner_proof field allows the current Owner to submit reg_enclave even if they are not the original creator. This is required after Transfer when the new Owner needs to update the Registry.

FieldRequiredDescription
sthYesSigned Tree Head from the enclave's sequencer
ct_proofYesCT inclusion proof binding state_hash to the signed root
state_hashYesSMT root hash at the proven tree position
events_rootYesMerkle root of event IDs in the bundle
smt_proofYesSMT membership proof showing from has owner trait
migrate_eventNoRequired if sequencer changed since Manifest (proves sequencer transition)

Authorization

The reg_enclave commit can be authorized in two ways:

Path 1: Original Creator (no owner_proof)
  • from field MUST equal manifest_event.from.
  • Registry verifies manifest_event.sig is valid.
Path 2: Current Owner (with owner_proof)
  • from field MAY differ from manifest_event.from.
  • owner_proof field MUST be present and valid.
  • Registry verifies the submitter currently holds the owner trait.
Manifest Signature Verification (both paths):

The manifest_event.sig signs the Manifest commit hash:

_content_hash = sha256(utf8_bytes(manifest_event.content))
commit_hash = H(0x10, enclave_id, from, "Manifest", _content_hash, exp, tags)
verify per manifest_event.alg (see §Signature Schemes):
  - "schnorr" (or absent) : schnorr_verify(commit_hash, sig, from)
  - "ecdsa"               : ecdsa_verify(commit_hash, sig, 0x02 || from)
Owner Proof Verification (Path 2 only):
  1. Verify STH signature:
    message = "enc:sth:" || be64(sth.t) || be64(sth.ts) || hex_decode(sth.r)
    verify: schnorr_verify(sha256(message), sth.sig, manifest_event.sequencer)
  2. Verify CT inclusion:
    • Compute leaf hash: H(0x00, events_root, state_hash)
    • Verify CT inclusion proof against sth.r using RFC 9162 algorithm
    • This binds state_hash to the signed tree
  3. Verify SMT proof:
    • Compute expected key: 0x00 || sha256(from)[0:160 bits] (RBAC namespace, 21 bytes total)
    • Verify smt_proof.k equals expected key
    • Verify SMT proof against state_hash
    • Verify smt_proof.v has owner trait (bit 8) set
  4. Verify enclave binding:
    • The manifest_event.enclave MUST match the enclave ID in Registry lookup.
    • This prevents using an owner proof from a different enclave
Post-Migration Updates:

After migration, the sequencer changes. To update Registry after migration:

  1. Include the migrate_event field in owner_proof:

    {
      "owner_proof": {
        "migrate_event": {
          "id": "<migrate_event_id>",
          "type": "Migrate",
          "content": { "new_sequencer": "<new_sequencer_pub>", ... },
          "sequencer": "<new_sequencer_pub>",
          "seq_sig": "<new_sequencer_signature>",
          ...
        },
        "sth": { ... },
        "ct_proof": { ... },
        ...
      }
    }
  2. Registry verifies the Migrate event:

    • migrate_event.content.new_sequencer = new sequencer public key
    • migrate_event.seq_sig is valid signature by new sequencer
    • migrate_event.enclave matches manifest_event.enclave
  3. STH signature is verified against migrate_event.sequencer (new sequencer)

Chained Migrations:

If multiple migrations occurred, only the most recent migrate_event is needed. The new sequencer's STH authenticates the current state, which includes the full history.

Security Notes:
  • The manifest_event provides enclave identity and original creator
  • The migrate_event (if present) proves sequencer transition
  • The SMT proof proves current Owner status
  • All three are cryptographically bound via signatures

Derived Fields

Registry extracts:

  • enclave_id = manifest_event.enclave
  • sequencer = owner_proof.migrate_event.content.new_sequencer if migrate_event is present, otherwise manifest_event.sequencer
  • creator = manifest_event.from

Update/Deregister

  • To update metadata: submit new reg_enclave (replaces previous)
  • To deregister: submit Delete event referencing the reg_enclave event

reg_identity

Type: reg_identity

Purpose: Register an identity's enclaves in the Registry for discovery.

Content Structure

{
  "id_pub": "a1b2c3...",
  "id_pub_full": "02a1b2c3...",
  "sub_pub": "d4e5f6...",
  "enclaves": {
    "personal": "<enclave_id>",
    "dm": "<enclave_id>"
  }
}

Fields

FieldRequiredDescription
id_pubYesIdentity public key (32-byte x-only)
id_pub_fullNoFull compressed secp256k1 public key (33 bytes, `02
sub_pubNo32-byte x-only operating public key for ECDH-incapable wallets (notably MetaMask EIP-191/ECDSA, whose private key cannot be exposed for ECDH). Derived deterministically by the wallet from id_pub + a stable salt; published here so peers can target this identity for ECDH-derived schemes (ratchet-pair OR-wrap, mls-lazy epoch_or_wraps, ecdh-envelope). For ECDH-capable wallets (dev / passkey / Nostr / NFC), sub_pub is identical to id_pub and MAY be omitted; absent sub_pub, peers MUST treat id_pub as the operating key. See Delegated Sub-keys.
enclavesNoMap of label → enclave_id (named enclave references)

Public Key Parity

The identity id_pub is a 32-byte x-only key, which omits the y-coordinate parity. For Schnorr verification this is sufficient (BIP-340 keys are even-y by definition). But operations that need the full curve point — deriving an EVM-style address keccak256(uncompressed_pubkey)[12:], or computing an ECDH shared secret from the compressed point — are ambiguous from id_pub alone, because two points (02||x and 03||x) share the same x.

Two ways to resolve the ambiguity:

  1. Even-y convention (default). Treat every id_pub as the even-y point (02||id_pub). A signer whose natural key is odd-y uses the BIP-340-adjusted (negated) private key, so the key it controls matches the even-y point others derive. This is consistent with the ECDSA verification rule (0x02 || id_pub) in Signature Schemes.
  2. Published id_pub_full. Registering the full compressed id_pub_full (33 bytes, 02||x or 03||x) records the actual parity, so peers derive the correct address / ECDH point without assuming even-y. For an identity registering via an ECDSA commit (alg: "ecdsa"), the registration signature's recovered public key MUST equal the registered id_pub_full, binding the parity to the key. When present, id_pub_full MUST satisfy x_only(id_pub_full) == id_pub.

id_pub_full is OPTIONAL; absent it, the even-y convention applies.

Enclaves Map

The enclaves field is a free-form map of string keys to enclave IDs. Applications use this to associate named enclaves with an identity.

Common keys:

  • personal — the identity's personal enclave (see Appendix A)

The map is application-defined; the Registry stores but does not interpret the keys.

Validation Limits

FieldMax
enclaves map256 entries
enclaves key64 characters

Nodes MUST reject reg_identity commits exceeding these limits.

Authorization

  • commit.from MUST equal content.id_pub (the parent identity).
  • commit.sig MUST verify via either of the two paths defined in §Strict Self-Authorization above for reg_identity: direct (parent's own Schnorr or ECDSA signature, dispatched by commit.alg) or sub-key (cosign cert with cert.parentId == id_pub + sub-key Schnorr over commit.hash). RBAC and the Sender check remain keyed to the parent regardless of which path produced the signature.

Update/Deregister

reg_identity uses the multi-event merge model defined in §DataView Semantics below. An identity MAY publish multiple reg_identity events for the same id_pub; DataView folds the active set with last-seq-wins per top-level field.

To add or change a key (e.g. register a new dm enclave, rotate sub_pub):

  • Submit a fresh reg_identity event with only the keys you're changing in the enclaves map. DataView merges it into the prior view; un-mentioned keys persist.

To remove a key from the enclaves map:

  • Submit Delete(event_id) referencing the event that contributed that key. Its contribution drops out of the merge. If the key was set by multiple active events, the latest surviving contributor wins.

To replace one specific past contribution (e.g. fix a typo in the value the old event published):

  • Submit Update(event_id, new_content). The old event's contribution is replaced wholesale by the Update's content; keys the old event set but the Update doesn't disappear from the merge.

To fully reset all registrations for an identity:

  • Submit Delete(event_id) for each active reg_identity event for that id_pub. After the last DELETE the merged view is empty.

There is no single "replace whole map" verb. The merge model treats each event as a contribution; replace-all is achieved by deleting prior contributions.



Confidentiality

The Registry is public by design. The manifest declares readers: [{ "type": "Public", "reads": "*" }], so every event is readable by any client; content is not encrypted. reg_node, reg_enclave, and reg_identity records are intended for discovery and MUST NOT carry secrets.

No confidentiality plugin is attached to this enclave.

Registry Operations

The rules below define Registry-specific semantics not covered by the generic enclave model.

Conflict Resolution

When two parties attempt to register the same enclave_id via reg_enclave:

  • First valid reg_enclave wins (earliest seq in Registry).
  • Subsequent registrations for the same enclave_id are rejected (not superseded).
  • To transfer an enclave to a different node, use Migrate (not Registry re-registration).

This differs from normal reg_enclave superseding behavior — enclave ID conflicts are errors, not updates.

DataView Semantics

Registry has three resource types with three different DataView reduce rules:

ResourceKeyReduceRationale
reg_nodeseq_publatest-wins (last active event for the key)A node has one current endpoint set; replacement is the natural verb
reg_enclaveenclave_idfirst-wins (initial registration; conflicts rejected)Enclave IDs are unforgeable; whoever registers first owns the slot. Transfer of hosting goes through Migrate, not Registry re-registration. See §Conflict Resolution.
reg_identityid_pubmulti-event merge (fold all active events, LWW per top-level field)An identity accumulates enclaves over time; partial updates should not erase prior keys

The merge rule below applies only to reg_identity. reg_node and reg_enclave keep their existing single-active-entry semantics.

reg_identity merge

For a given id_pub X, the DataView projection is computed by:

events_X = [e ∈ Registry events
           | e.type == "reg_identity"
           , e.content.id_pub == X
           , status(e) == Active ]    -- not Deleted, not Updated
ordered  = sort events_X by e.seq ascending
view(X)  = fold ordered with mergeMaps (shallow per top-level key, last-write-wins)

Where mergeMaps is shallow — top-level fields (enclaves, id_pub_full, sub_pub) merge per-field LWW, and the enclaves map itself merges per-key LWW. Nested objects deeper than the enclaves map are NOT merged structurally.

Example:
seqeventcontentmerged view after event
100reg_identity (CREATE){id_pub:X, enclaves:{personal:"p1", dm:"d1"}}{enclaves:{personal:"p1", dm:"d1"}}
150reg_identity (CREATE){id_pub:X, enclaves:{group:"g1"}, sub_pub:"sp"}{enclaves:{personal:"p1", dm:"d1", group:"g1"}, sub_pub:"sp"}
200reg_identity (CREATE){id_pub:X, enclaves:{dm:"d2"}}{enclaves:{personal:"p1", dm:"d2", group:"g1"}, sub_pub:"sp"}
250Delete(event_at_seq_100){enclaves:{dm:"d2", group:"g1"}, sub_pub:"sp"} (personal vanishes — only seq 100 set it)
300Update(event_at_seq_150, {enclaves:{group:"g2"}})replaces seq 150's contribution{enclaves:{dm:"d2", group:"g2"}} (sub_pub vanishes — only seq 150 set it, now superseded by Update content that omits it)

Status interpretation: active = not in the SMT's deleted/updated lifecycle for that event id. See spec.md §U (Update) and D (Delete) for the canonical event status model.

Ordering tiebreak: seq is strictly monotonic per enclave (sequencer-linearized), so per-key LWW has no ambiguity.

Implementation: re-fold on read vs. cache

Two equivalent implementations:

Option A — re-fold on read. DataView keeps no cache. On GET /identity/:id_pub, walk the active reg_identity events for that id_pub and fold. O(n) per read where n = active events for that id_pub. No invalidation logic; impossible to have stale state. Recommended for the reference impl.

Option B — cached merged view. DataView keeps merged_view: Map<id_pub, MergedContent>. On any reg_identity event (CREATE / UPDATE / DELETE) for id_pub X, recompute X's entry from active events. O(1) read, O(n) write but only on events affecting X. Recommended for production nodes that prioritize read latency.

Both produce the same projection. Conformance is defined against the re-fold semantics; caches MUST agree with the re-fold result event-by-event.

SMT entry for reg_identity

The SMT records per-event status (Active / Deleted / Updated) as for any other event — see spec.md §U (Update) and D (Delete). The Registry node MAY additionally publish a per-identity merged-state hash under SMT[registry:identity:<id_pub>] = sha256(canonical(view(id_pub))) for clients that want a verifiable summary of an identity's current registration without replaying the event chain. This is OPTIONAL and additive — clients can always recompute by reading the event log.

Querying

Clients query Registry by key:

  • reg_node: GET /nodes/:seq_pub → latest active reg_node event for that seq_pub
  • reg_enclave: GET /enclaves/:enclave_id → the unique active reg_enclave event for that enclave_id (first-wins)
  • reg_identity: GET /identity/:id_pub → the merged view of all active reg_identity events for that id_pub

Registry Governance

Registry Owner:

The registry_owner_id is the identity operating the Registry service. In the current centralized design:

  • The Registry Owner is a fixed, well-known identity (e.g., protocol operator)
  • The Registry Owner has standard Owner powers: can Transfer, Pause, Resume, Terminate
  • The Registry Owner does NOT have special powers over individual reg_node/reg_enclave/reg_identity entries — those are controlled by their respective Self identities
Notes:
  • Current design is centralized; future versions can be decentralized.

The Process of Registry

  1. Node Register: The node normally registers seq_pub and domain / IP on the registry with reg_node. If both domain and IP is set, first resolve IP then the domain.
  2. Create Enclave: The client send manifest to the node, and get receipt with sequencer, then the client knows who is hosting the enclave.
  3. Enclave Register: Then the client can send the finalized event as content of the register commit (event type predefined as reg_enclave). And the registry will check the sig = event.sig in content. If it comes from the same signer, registry will accept this register.
  4. Identity Register: The client registers its enclaves via reg_identity, mapping id_pub → enclaves. The commit's from MUST equal content.id_pub (the parent identity), and commit.sig is either the parent's direct signature OR a sub-key Schnorr signature accompanied by a cosign cert whose parentId equals id_pub. This step is optional but enables discovery of an identity's enclaves; it is the only step that publishes sub_pub and id_pub_full, both of which peers need for ECDH-derived encryption (ratchet-pair, mls-lazy OR-wrap, ecdh-envelope) and for parity-aware key operations.

DataView API

Originally specified in node-api.md; moved here because these endpoints are Registry-specific, not generic node API. The Registry enclave's DataView exposes the endpoints below.

The Registry is an enclave with a DataView server that provides discovery endpoints. These endpoints are served by the Registry's DataView, not by the generic Enclave API.

Base URL: Registry node endpoint (discovered via bootstrap)

GET /nodes/

Resolve node by sequencer public key.

Path Parameters:
ParamTypeDescription
seq_pubhex64Sequencer public key
Request:
GET /nodes/a1b2c3...
Response (200 OK):
{
  "seq_pub": "<hex64>",
  "endpoints": [
    { "uri": "https://node.example.com", "priority": 1 },
    { "uri": "https://backup.example.com", "priority": 2 }
  ],
  "protocols": ["https", "wss"],
  "enc_v": 2
}
FieldTypeDescription
seq_pubhex64Sequencer public key
endpointsarrayEndpoints sorted by priority (1 = highest)
endpoints[].uristringEndpoint URI
endpoints[].priorityuintPriority (lower = preferred)
protocolsarraySupported protocols
enc_vuintENC protocol version
Errors:
CodeHTTPDescription
NODE_NOT_FOUND404Node not registered

GET /enclaves/

Resolve an enclave: returns the enclave record and its current hosting node, so a client can connect in a single round-trip (no separate /nodes follow-up needed).

Path Parameters:
ParamTypeDescription
enclave_idhex64Enclave identifier
Request:
GET /enclaves/d4e5f6...
Response (200 OK):
{
  "enclave": {
    "enclave_id": "<hex64>",
    "sequencer": "<hex64>",
    "creator": "<hex64>",
    "created_at": 1706000000000,
    "app": "chat",
    "desc": "Team chat",
    "meta": {}
  },
  "node": {
    "seq_pub": "<hex64>",
    "endpoints": [
      { "uri": "https://node.example.com", "priority": 1 }
    ],
    "protocols": ["https", "wss"],
    "enc_v": 2
  }
}
FieldTypeRequiredDescription
enclave.enclave_idhex64YesEnclave identifier
enclave.sequencerhex64YesCurrent sequencer public key (equals node.seq_pub)
enclave.creatorhex64NoCreator's identity key
enclave.created_atuintNoCreation timestamp (Unix milliseconds)
enclave.appstringNoApplication identifier
enclave.descstringNoHuman-readable description
enclave.metaobjectNoApplication-defined metadata
node.seq_pubhex64YesSequencer public key hosting the enclave
node.endpointsarrayYesHosting node endpoints (uri + priority)
node.protocolsarrayNoSupported transports
node.enc_vuintNoProtocol version
Errors:
CodeHTTPDescription
ENCLAVE_NOT_FOUND404Enclave not registered
NODE_NOT_FOUND404Sequencer node not registered

GET /identity/

Resolve identity by public key. Returns the identity's registered enclaves.

Path Parameters:
ParamTypeDescription
id_pubhex64Identity public key
Request:
GET /identity/a1b2c3...
Response (200 OK):
{
  "id_pub": "<hex64>",
  "enclaves": {
    "personal": "<enclave_id>",
    "dm": "<enclave_id>"
  }
}
FieldTypeDescription
id_pubhex64Identity public key
enclavesobjectMap of label → enclave_id
Errors:
CodeHTTPDescription
IDENTITY_NOT_FOUND404Identity not registered