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
- Registry Purpose
- Manifest
- State Transitions
- Event-Operator Matrix
- Content Events
- Confidentiality
- Registry Operations
- 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
A registry entry MAY include:
enclave_id— canonical enclave identifiernodes— hosting node endpoints or identifiersapp(optional) — application that created or uses the enclavecreator(optional) — identity key that initialized the enclavedesc(optional) — human-readable descriptionmeta(optional) — application-defined metadata
- 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"] }
]
}- 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)
Senderfor reg_node is evaluated againstcontent.seq_pub.Senderfor reg_enclave is evaluated againstcontent.manifest_event.from.Senderfor reg_identity is evaluated againstcontent.id_pub.- owner trait can Grant/Revoke the dataview trait (manage push endpoints)
- owner trait can Transfer ownership and Terminate
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:
- Direct.
commit.sigis the parent identity's own signature overcommit.hash, dispatched percommit.alg(Schnorr or ECDSA — see §Signature Schemes). - Sub-key via cosign cert.
commit.certcarries a valid Delegated Sub-keys cert,commit.cert.parentId == commit.from == content.id_pub, andcommit.sigis the sub-key's Schnorr signature overcommit.hash. Cert-authorized reg_identity is the path used by MetaMask-style ECDSA-only wallets that publish theirsub_pubthrough 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.
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.
| Scenario | from field | owner_proof |
|---|---|---|
| Original creator updates | manifest_event.from | Not required |
| New Owner updates (no migration) | New Owner's key | Required (SMT proof of owner trait) |
| New Owner updates (after migration) | New Owner's key | Required (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:
| Event | Operator | Ops | Notes |
|---|---|---|---|
| Grant | owner | C | Issues dataview trait to a node serving the REST API |
| Revoke | owner | C | Revokes dataview trait |
| Transfer | owner | C | Transfers owner trait to a new identity |
| Terminate | owner | C | Lifecycle: closes the Registry enclave |
| reg_node | Public | C | Anyone may submit; signed by seq_pub |
| reg_node | Sender | U, D | Only the original submitter (from == content.seq_pub) |
| reg_node | dataview | P | Pushed to the DataView server |
| reg_enclave | Public | C | Anyone may submit; signed by enclave creator or current owner |
| reg_enclave | Sender | U, D | Only the original submitter (from == content.manifest_event.from) |
| reg_enclave | dataview | P | Pushed to the DataView server |
| reg_identity | Public | C | Anyone may submit; signed by id_pub (parent or sub-key path) |
| reg_identity | Sender | U, D | Only the original submitter (from == content.id_pub) |
| reg_identity | dataview | P | Pushed to the DataView server |
| Read | Public (reads: "*") | R | All 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
| Field | Required | Description |
|---|---|---|
| seq_pub | Yes | Node's sequencer public key |
| endpoints | Yes | Array of endpoints, ordered by priority (lower = preferred) |
| endpoints[].uri | Yes | Full URI including protocol and port |
| endpoints[].priority | No | Resolution order (default: array index) |
| protocols | No | Supported transport protocols |
| enc_v | Yes | ENC protocol version |
Validation Limits
| Field | Max |
|---|---|
| endpoints array | 10 entries |
| endpoints[].uri | 2048 characters |
| protocols array | 10 entries |
Nodes MUST reject reg_node commits exceeding these limits.
Authorization
- The commit MUST be signed by
seq_pub(proves ownership). fromfield MUST equalseq_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
| Field | Required | Description |
|---|---|---|
| manifest_event | Yes | The finalized Manifest event (full event structure) |
| owner_proof | No | Proof of current Owner status (required if from ≠ manifest_event.from) |
| app | No | Application identifier |
| desc | No | Human-readable description |
| meta | No | Application-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.
| Field | Required | Description |
|---|---|---|
| sth | Yes | Signed Tree Head from the enclave's sequencer |
| ct_proof | Yes | CT inclusion proof binding state_hash to the signed root |
| state_hash | Yes | SMT root hash at the proven tree position |
| events_root | Yes | Merkle root of event IDs in the bundle |
| smt_proof | Yes | SMT membership proof showing from has owner trait |
| migrate_event | No | Required if sequencer changed since Manifest (proves sequencer transition) |
Authorization
The reg_enclave commit can be authorized in two ways:
fromfield MUST equalmanifest_event.from.- Registry verifies
manifest_event.sigis valid.
fromfield MAY differ frommanifest_event.from.owner_prooffield MUST be present and valid.- Registry verifies the submitter currently holds the owner trait.
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)-
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) -
Verify CT inclusion:
- Compute leaf hash:
H(0x00, events_root, state_hash) - Verify CT inclusion proof against
sth.rusing RFC 9162 algorithm - This binds
state_hashto the signed tree
- Compute leaf hash:
-
Verify SMT proof:
- Compute expected key:
0x00 || sha256(from)[0:160 bits](RBAC namespace, 21 bytes total) - Verify
smt_proof.kequals expected key - Verify SMT proof against
state_hash - Verify
smt_proof.vhas owner trait (bit 8) set
- Compute expected key:
-
Verify enclave binding:
- The
manifest_event.enclaveMUST match the enclave ID in Registry lookup. - This prevents using an owner proof from a different enclave
- The
After migration, the sequencer changes. To update Registry after migration:
-
Include the
migrate_eventfield inowner_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": { ... }, ... } } -
Registry verifies the Migrate event:
migrate_event.content.new_sequencer= new sequencer public keymigrate_event.seq_sigis valid signature by new sequencermigrate_event.enclavematchesmanifest_event.enclave
-
STH signature is verified against
migrate_event.sequencer(new sequencer)
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.
- The
manifest_eventprovides 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.enclavesequencer=owner_proof.migrate_event.content.new_sequencerifmigrate_eventis present, otherwisemanifest_event.sequencercreator=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
| Field | Required | Description |
|---|---|---|
| id_pub | Yes | Identity public key (32-byte x-only) |
| id_pub_full | No | Full compressed secp256k1 public key (33 bytes, `02 |
| sub_pub | No | 32-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. |
| enclaves | No | Map 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:
- Even-y convention (default). Treat every
id_pubas 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. - Published
id_pub_full. Registering the full compressedid_pub_full(33 bytes,02||xor03||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 registeredid_pub_full, binding the parity to the key. When present,id_pub_fullMUST satisfyx_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
| Field | Max |
|---|---|
| enclaves map | 256 entries |
| enclaves key | 64 characters |
Nodes MUST reject reg_identity commits exceeding these limits.
Authorization
commit.fromMUST equalcontent.id_pub(the parent identity).commit.sigMUST 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 bycommit.alg) or sub-key (cosign cert withcert.parentId == id_pub+ sub-key Schnorr overcommit.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
enclavesmap. 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 thatid_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
seqin Registry). - Subsequent registrations for the same
enclave_idare 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:
| Resource | Key | Reduce | Rationale |
|---|---|---|---|
| reg_node | seq_pub | latest-wins (last active event for the key) | A node has one current endpoint set; replacement is the natural verb |
| reg_enclave | enclave_id | first-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_identity | id_pub | multi-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.
| seq | event | content | merged view after event |
|---|---|---|---|
| 100 | reg_identity (CREATE) | {id_pub:X, enclaves:{personal:"p1", dm:"d1"}} | {enclaves:{personal:"p1", dm:"d1"}} |
| 150 | reg_identity (CREATE) | {id_pub:X, enclaves:{group:"g1"}, sub_pub:"sp"} | {enclaves:{personal:"p1", dm:"d1", group:"g1"}, sub_pub:"sp"} |
| 200 | reg_identity (CREATE) | {id_pub:X, enclaves:{dm:"d2"}} | {enclaves:{personal:"p1", dm:"d2", group:"g1"}, sub_pub:"sp"} |
| 250 | Delete(event_at_seq_100) | — | {enclaves:{dm:"d2", group:"g1"}, sub_pub:"sp"} (personal vanishes — only seq 100 set it) |
| 300 | Update(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
Selfidentities
- Current design is centralized; future versions can be decentralized.
The Process of Registry
- 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.
- Create Enclave: The client send manifest to the node, and get receipt with sequencer, then the client knows who is hosting the enclave.
- 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.
- Identity Register: The client registers its enclaves via reg_identity, mapping
id_pub → enclaves. The commit'sfromMUST equalcontent.id_pub(the parent identity), andcommit.sigis either the parent's direct signature OR a sub-key Schnorr signature accompanied by a cosign cert whoseparentIdequalsid_pub. This step is optional but enables discovery of an identity's enclaves; it is the only step that publishessub_pubandid_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:| Param | Type | Description |
|---|---|---|
| seq_pub | hex64 | Sequencer public key |
GET /nodes/a1b2c3...{
"seq_pub": "<hex64>",
"endpoints": [
{ "uri": "https://node.example.com", "priority": 1 },
{ "uri": "https://backup.example.com", "priority": 2 }
],
"protocols": ["https", "wss"],
"enc_v": 2
}| Field | Type | Description |
|---|---|---|
| seq_pub | hex64 | Sequencer public key |
| endpoints | array | Endpoints sorted by priority (1 = highest) |
| endpoints[].uri | string | Endpoint URI |
| endpoints[].priority | uint | Priority (lower = preferred) |
| protocols | array | Supported protocols |
| enc_v | uint | ENC protocol version |
| Code | HTTP | Description |
|---|---|---|
NODE_NOT_FOUND | 404 | Node 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).
| Param | Type | Description |
|---|---|---|
| enclave_id | hex64 | Enclave identifier |
GET /enclaves/d4e5f6...{
"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
}
}| Field | Type | Required | Description |
|---|---|---|---|
| enclave.enclave_id | hex64 | Yes | Enclave identifier |
| enclave.sequencer | hex64 | Yes | Current sequencer public key (equals node.seq_pub) |
| enclave.creator | hex64 | No | Creator's identity key |
| enclave.created_at | uint | No | Creation timestamp (Unix milliseconds) |
| enclave.app | string | No | Application identifier |
| enclave.desc | string | No | Human-readable description |
| enclave.meta | object | No | Application-defined metadata |
| node.seq_pub | hex64 | Yes | Sequencer public key hosting the enclave |
| node.endpoints | array | Yes | Hosting node endpoints (uri + priority) |
| node.protocols | array | No | Supported transports |
| node.enc_v | uint | No | Protocol version |
| Code | HTTP | Description |
|---|---|---|
ENCLAVE_NOT_FOUND | 404 | Enclave not registered |
NODE_NOT_FOUND | 404 | Sequencer node not registered |
GET /identity/
Resolve identity by public key. Returns the identity's registered enclaves.
Path Parameters:| Param | Type | Description |
|---|---|---|
| id_pub | hex64 | Identity public key |
GET /identity/a1b2c3...{
"id_pub": "<hex64>",
"enclaves": {
"personal": "<enclave_id>",
"dm": "<enclave_id>"
}
}| Field | Type | Description |
|---|---|---|
| id_pub | hex64 | Identity public key |
| enclaves | object | Map of label → enclave_id |
| Code | HTTP | Description |
|---|---|---|
IDENTITY_NOT_FOUND | 404 | Identity not registered |