Working with XID Edges
February 5, 2026 · View on GitHub
XID Edges are verifiable claims that connect XID entities. They allow one entity (the source) to make a signed statement about another entity (the target), or about itself. Edges are defined in BCR-2026-003.
An edge is a Gordian Envelope with:
- A subject — a locally unique identifier for the claim
- Exactly three assertions:
'isA'— the type of claim'source'— the XID of the entity making the claim'target'— the XID of the entity the claim is about
No other assertions are permitted on the edge subject. The substance of the claim — names, dates, roles, or any other detail — is carried as assertions on the target object.
Setup
We'll use Alice and Bob for our examples. First, set up their XID documents:
ALICE_PUBKEYS="ur:crypto-pubkeys/lftanshfhdcxrdhgfsfsfsosrloebgwmfrfhsnlskegsjydecawybniadyzovehncacnlbmdbesstansgrhdcxytgefrmnbzftltcmcnaspaimhftbjehlatjklkhktidrpmjobslewkfretcaetbnwksorlbd"
ALICE_PRVKEYS="ur:crypto-prvkeys/lftansgohdcxdntswmjerdqdoxhnguzsdrhfcmjsfewkhkvezohkeycpasdysrvdgypeoemtgywztansgehdcxisespmvlhflnweksvyfnmhvofysnhyztpyhlftluweaoemenurstreckoybbfroektnncyls"
BOB_PUBKEYS="ur:crypto-pubkeys/lftanshfhdcxndctnnflynethhhnwdkbhtehhdosmhgoclvefhjpehtaethkltsrmssnwfctfggdtansgrhdcxtipdbagmoertsklaflfhfewsptrlmhjpdeemkbdyktmtfwnninfrbnmwonetwphejzwnmhhf"
XID_DOC=$(envelope xid new "$ALICE_PRVKEYS")
ALICE_XID=$(envelope xid id "$XID_DOC")
BOB_DOC=$(envelope xid new "$BOB_PUBKEYS")
BOB_XID=$(envelope xid id "$BOB_DOC")
Edge Structure
An edge envelope has a string subject (the claim identifier) and exactly three required assertions. Here is how to create a minimal edge using the envelope command:
EDGE=$(envelope subject type string "self-description")
EDGE=$(envelope assertion add pred-obj known isA string "foaf:Person" "$EDGE")
EDGE=$(envelope assertion add pred-obj known source ur "$ALICE_XID" "$EDGE")
EDGE=$(envelope assertion add pred-obj known target ur "$ALICE_XID" "$EDGE")
envelope format "$EDGE"
│ "self-description" [
│ 'isA': "foaf:Person"
│ 'source': XID(93a4d4e7)
│ 'target': XID(93a4d4e7)
│ ]
In this self-description edge, Alice is both the source (claimant) and the target (subject of the claim). The edge identifies what kind of claim is being made ('isA'), by whom ('source'), and about whom ('target'), but it doesn't yet carry any detail about the claim itself. The next section shows how to add that detail.
Adding Claim Detail to the Target
Per BCR-2026-003, the substance of a claim is carried as assertions on the target object, not on the edge subject. To do this, build a target envelope with assertions before adding it to the edge.
First, create the target envelope from Alice's XID and add claim-detail assertions to it:
TARGET=$(envelope subject type ur "$ALICE_XID")
TARGET=$(envelope assertion add pred-obj string "foaf:firstName" string "Alice" "$TARGET")
TARGET=$(envelope assertion add pred-obj string "foaf:lastName" string "Smith" "$TARGET")
envelope format "$TARGET"
│ XID(93a4d4e7) [
│ "foaf:firstName": "Alice"
│ "foaf:lastName": "Smith"
│ ]
Now build the edge using this enriched target. Use envelope as the type so the target's assertions are preserved:
EDGE=$(envelope subject type string "self-description")
EDGE=$(envelope assertion add pred-obj known isA string "foaf:Person" "$EDGE")
EDGE=$(envelope assertion add pred-obj known source ur "$ALICE_XID" "$EDGE")
EDGE=$(envelope assertion add pred-obj known target envelope "$TARGET" "$EDGE")
envelope format "$EDGE"
│ "self-description" [
│ 'isA': "foaf:Person"
│ 'source': XID(93a4d4e7)
│ 'target': XID(93a4d4e7) [
│ "foaf:firstName": "Alice"
│ "foaf:lastName": "Smith"
│ ]
│ ]
The edge still has exactly three assertions on its subject ('isA', 'source', 'target'), so it passes validation. The claim detail lives on the target object, where verifiers expect to find it.
Using Known Values for Ontological Concepts
The examples above use plain strings (string "foaf:Person") for the 'isA' claim type. This works, but Gordian Envelope supports known values — compact integer-based identifiers that map to ontological concepts from standard vocabularies like FOAF, Schema.org, Dublin Core, and others.
When known values are available, you can use known instead of string for the 'isA' object:
EDGE_KV=$(envelope subject type string "self-description")
EDGE_KV=$(envelope assertion add pred-obj known isA known "foaf:Person" "$EDGE_KV")
EDGE_KV=$(envelope assertion add pred-obj known source ur "$ALICE_XID" "$EDGE_KV")
EDGE_KV=$(envelope assertion add pred-obj known target ur "$ALICE_XID" "$EDGE_KV")
envelope format "$EDGE_KV"
│ "self-description" [
│ 'isA': 'foaf:Person'
│ 'source': XID(93a4d4e7)
│ 'target': XID(93a4d4e7)
│ ]
Notice that the output shows 'foaf:Person' (single-quoted known value) rather than "foaf:Person" (double-quoted string). Known values are encoded as integers in the envelope, making them more compact and unambiguous than strings.
Note:
Some known values are built in to the
envelopetool, and you've already seen some used above, like'isA','source', and'target'. Many available known values are not built in and are resolved from JSON registry files in~/.known-values/. If the registry files for a given ontology are not installed, usingknown "foaf:Person"will produce an error.The remainder of this tutorial uses strings for portability, but known values are preferred when the registries are available.
- BCR-2023-002 describes the known values system in detail.
- The JSON registry files can be found here.
Adding Edges to XID Documents
Use xid edge add to add an edge to a XID document:
XID_DOC=$(envelope xid edge add "$EDGE" "$XID_DOC")
envelope format "$XID_DOC"
│ XID(93a4d4e7) [
│ 'edge': "self-description" [
│ 'isA': "foaf:Person"
│ 'source': XID(93a4d4e7)
│ 'target': XID(93a4d4e7) [
│ "foaf:firstName": "Alice"
│ "foaf:lastName": "Smith"
│ ]
│ ]
│ 'key': PublicKeys(cab108a0, ...) [
│ ...
│ ]
│ ]
Counting Edges
envelope xid edge count "$XID_DOC"
│ 1
Adding a Relationship Edge
A relationship edge connects two different XIDs. Here Alice claims Bob as a colleague. The claim detail — Bob's name and department — goes on the target:
BOB_TARGET=$(envelope subject type ur "$BOB_XID")
BOB_TARGET=$(envelope assertion add pred-obj string "foaf:firstName" string "Bob" "$BOB_TARGET")
BOB_TARGET=$(envelope assertion add pred-obj string "department" string "Engineering" "$BOB_TARGET")
EDGE2=$(envelope subject type string "knows-bob")
EDGE2=$(envelope assertion add pred-obj known isA string "schema:colleague" "$EDGE2")
EDGE2=$(envelope assertion add pred-obj known source ur "$ALICE_XID" "$EDGE2")
EDGE2=$(envelope assertion add pred-obj known target envelope "$BOB_TARGET" "$EDGE2")
envelope format "$EDGE2"
│ "knows-bob" [
│ 'isA': "schema:colleague"
│ 'source': XID(93a4d4e7)
│ 'target': XID(f1199a75) [
│ "department": "Engineering"
│ "foaf:firstName": "Bob"
│ ]
│ ]
XID_DOC=$(envelope xid edge add "$EDGE2" "$XID_DOC")
envelope xid edge count "$XID_DOC"
│ 2
Retrieving Edges
Get All Edges
envelope xid edge all "$XID_DOC"
This outputs one UR string per line, one for each edge.
Get Edge at Index
envelope xid edge at 0 "$XID_DOC"
Returns the edge at the specified zero-based index.
Finding Edges
The xid edge find command filters edges by optional criteria. All criteria are optional and can be combined.
Find by Type
IS_A=$(envelope subject type string "foaf:Person")
envelope xid edge find --is-a "$IS_A" "$XID_DOC"
Returns only edges whose 'isA' value matches.
Find by Source
SOURCE=$(envelope subject type ur "$ALICE_XID")
envelope xid edge find --source "$SOURCE" "$XID_DOC"
Find by Target
TARGET_FILTER=$(envelope subject type ur "$BOB_XID")
envelope xid edge find --target "$TARGET_FILTER" "$XID_DOC"
Find by Subject Identifier
SUBJ=$(envelope subject type string "self-description")
envelope xid edge find --subject "$SUBJ" "$XID_DOC"
Combined Filters
Multiple filters narrow the results. Only edges matching all criteria are returned:
envelope xid edge find --is-a "$IS_A" --subject "$SUBJ" "$XID_DOC"
Edges Persist Across Operations
Once added, edges persist through other XID document operations:
XID_DOC=$(envelope xid method add "https://example.com/resolve" "$XID_DOC")
envelope xid edge count "$XID_DOC"
│ 2
Removing Edges
XID_DOC=$(envelope xid edge remove "$EDGE" "$XID_DOC")
envelope xid edge count "$XID_DOC"
│ 1
The edge to remove is identified by its exact envelope.
Working with Signed XID Documents
When working with signed XID documents, use --sign inception to maintain signature integrity:
XID_DOC=$(envelope xid new "$ALICE_PRVKEYS")
ALICE_XID=$(envelope xid id "$XID_DOC")
TARGET=$(envelope subject type ur "$ALICE_XID")
TARGET=$(envelope assertion add pred-obj string "foaf:firstName" string "Alice" "$TARGET")
TARGET=$(envelope assertion add pred-obj string "foaf:lastName" string "Smith" "$TARGET")
EDGE=$(envelope subject type string "self-description")
EDGE=$(envelope assertion add pred-obj known isA string "foaf:Person" "$EDGE")
EDGE=$(envelope assertion add pred-obj known source ur "$ALICE_XID" "$EDGE")
EDGE=$(envelope assertion add pred-obj known target envelope "$TARGET" "$EDGE")
XID_DOC=$(envelope xid edge add "$EDGE" --sign inception "$XID_DOC")
envelope xid id --verify inception "$XID_DOC"
The edge is included in the signature, so modifying or removing edges requires re-signing.
Removing with Re-signing
XID_DOC=$(envelope xid edge remove "$EDGE" --verify inception --sign inception "$XID_DOC")
Third-Party Credentials
A third party (e.g., a university) can create and sign an edge, which the target entity then adds to their own XID document. This allows verifiable credentials from external issuers.
The edge should be constructed by the issuer, wrapped and signed with their key, then provided to the target entity for inclusion. The target object carries the credential details:
# Issuer builds the target with credential detail
CREDENTIAL_TARGET=$(envelope subject type ur "$BOB_XID")
CREDENTIAL_TARGET=$(envelope assertion add pred-obj string "schema:name" string "Master of Science in Computer Science" "$CREDENTIAL_TARGET")
CREDENTIAL_TARGET=$(envelope assertion add pred-obj string "schema:credentialCategory" string "degree" "$CREDENTIAL_TARGET")
# Issuer creates the edge
CREDENTIAL=$(envelope subject type string "degree-2024")
CREDENTIAL=$(envelope assertion add pred-obj known isA string "schema:EducationalOccupationalCredential" "$CREDENTIAL")
CREDENTIAL=$(envelope assertion add pred-obj known source ur "$ISSUER_XID" "$CREDENTIAL")
CREDENTIAL=$(envelope assertion add pred-obj known target envelope "$CREDENTIAL_TARGET" "$CREDENTIAL")
SIGNED_CREDENTIAL=$(envelope sign --signer "$ISSUER_PRVKEYS" "$CREDENTIAL")
# Bob adds the signed credential to his XID document
BOB_DOC=$(envelope xid edge add "$SIGNED_CREDENTIAL" "$BOB_DOC")
The signed edge remains independently verifiable even when extracted from the XID document.