Relationships

The Relationships domain is the graph layer of the EDM. A Relationship is a typed, directed edge between any two entities. You can link a Person to a TechnologyAsset, a Vulnerability to a CloudResource, or a RiskItem to a ComplianceControl — and then traverse those connections.


Relationship Schema

FieldTypeRequiredDescription
idstringautoPrefixed ID: rel-{ulid}
fromIdstringyesSource entity ID
fromTypestringyesEntity type (e.g. person, group, cloudResource)
toIdstringyesTarget entity ID
toTypestringyesEntity type
relationstringyesRelationship label (see below)
metadataobjectnoArbitrary JSON key-value pairs
createdAtstringautoISO 8601 UTC

Built-in Relation Types

RelationMeaning
member_ofPerson or Group is a member of a Group
ownsPerson owns an Asset or TechnologyAsset
assigned_toAsset is assigned to a Person
has_entitlementPerson holds an AccessEntitlement
has_licensePerson is assigned a SoftwareLicense
affectsVulnerability affects an Asset
mitigatesControl or RiskItem mitigates a Vulnerability
evidencesArtifact evidences a ComplianceControl
depends_onAsset depends on another Asset
reports_toPerson reports to another Person
customUser-defined — set metadata.label for display

Create a Relationship

import { CpodClient } from '@cpod/sdk'
const sdk = CpodClient.fromEnv()
 
// Alice has admin entitlement on GitHub Enterprise
const rel = await sdk.relationships.create({
  fromId: 'per-alice123',
  fromType: 'person',
  toId: 'ent-github-admin',
  toType: 'accessEntitlement',
  relation: 'has_entitlement',
  metadata: { grantedBy: 'per-manager456', grantedAt: '2024-06-01' },
})
// rel.id → "rel-01HXYZ..."
 
// Vulnerability affects a cloud resource
await sdk.relationships.create({
  fromId: 'vul-abc123',
  fromType: 'vulnerability',
  toId: 'cld-xyz789',
  toType: 'cloudResource',
  relation: 'affects',
})

Query Relationships

Find all relationships from or to a given entity, optionally filtered by relation type or the type of the connected entity.

// All entitlements Alice holds
const rels = await sdk.relationships.list({
  fromId: 'per-alice123',
  fromType: 'person',
  relation: 'has_entitlement',
  limit: 50,
})
 
for (const r of rels.items) {
  console.log(r.toType, r.toId, r.metadata)
}
 
// All people who have access to a specific entitlement
const holders = await sdk.relationships.list({
  toId: 'ent-github-admin',
  toType: 'accessEntitlement',
  relation: 'has_entitlement',
})
 
// All vulnerabilities affecting a specific asset
const vulns = await sdk.relationships.list({
  toId: 'cld-xyz789',
  toType: 'cloudResource',
  relation: 'affects',
})

Get & Delete a Relationship

const rel = await sdk.relationships.get('rel-abc123')
 
// Revoke an entitlement
await sdk.relationships.delete('rel-abc123')

Relationships do not cascade. Deleting a Person does not automatically remove their Relationship edges. Clean up edges explicitly before or after deleting an entity, or use the purge flag on delete: sdk.people.delete('per-abc123', { purgeRelationships: true }).


Traversal Queries

For multi-hop graph traversal — e.g. “all vulnerabilities affecting assets owned by people in this group” — use the traverse API:

const result = await sdk.relationships.traverse({
  startId: 'grp-engineering',
  startType: 'group',
  steps: [
    { relation: 'member_of', direction: 'inbound', targetType: 'person' },
    { relation: 'assigned_to', direction: 'inbound', targetType: 'cloudResource' },
    { relation: 'affects', direction: 'inbound', targetType: 'vulnerability' },
  ],
  limit: 100,
})
// result.nodes → Vulnerability[]
// result.paths → full path arrays for provenance