Data Model & Storage¶
This document explains the data model and storage architecture used by OWASP Amass. It covers the Open Asset Model (OAM) specification that defines asset types and relationships, the three-tier storage architecture (graph database, cache, and work queue), and how assets flow through the system from discovery to persistent storage.
Data Model Foundation¶
Amass uses the Open Asset Model (OAM) as its core data specification. OAM is a standardized schema for representing cybersecurity assets and their relationships in a graph structure. Each discovered piece of information (domain, IP address, organization, service, etc.) is represented as an OAM asset type.
OAM Asset Types¶
The system supports 20 distinct asset types, each implementing the oam.Asset interface:
graph TB
subgraph "Network Assets"
FQDN["FQDN<br/>oamdns.FQDN"]
IPAddress["IPAddress<br/>network.IPAddress"]
Netblock["Netblock<br/>network.Netblock"]
AS["AutonomousSystem<br/>network.AutonomousSystem"]
end
subgraph "Organization Assets"
Org["Organization<br/>org.Organization"]
Person["Person<br/>people.Person"]
Account["Account<br/>account.Account"]
end
subgraph "Infrastructure Assets"
Service["Service<br/>platform.Service"]
Product["Product<br/>platform.Product"]
Release["ProductRelease<br/>platform.ProductRelease"]
TLS["TLSCertificate<br/>oamcert.TLSCertificate"]
end
subgraph "Registration Assets"
DomainRec["DomainRecord<br/>oamreg.DomainRecord"]
IPNetRec["IPNetRecord<br/>oamreg.IPNetRecord"]
AutnumRec["AutnumRecord<br/>oamreg.AutnumRecord"]
end
subgraph "Contact Assets"
Contact["ContactRecord<br/>contact.ContactRecord"]
Location["Location<br/>contact.Location"]
Phone["Phone<br/>contact.Phone"]
end
subgraph "Other Assets"
URL["URL<br/>url.URL"]
File["File<br/>file.File"]
Transfer["FundsTransfer<br/>financial.FundsTransfer"]
ID["Identifier<br/>general.Identifier"]
end
Entity Wrapper Pattern¶
OAM assets are not stored directly. Instead, they are wrapped in a dbt.Entity object that provides metadata and tracking:
graph LR
Asset["oam.Asset<br/>(FQDN, IPAddress, etc.)"]
Entity["dbt.Entity"]
ID["ID: string<br/>(unique identifier)"]
CreatedAt["CreatedAt: time.Time"]
Entity --> Asset
Entity --> ID
Entity --> CreatedAt
Event["et.Event"] --> Entity
Cache["cache.Cache"] --> Entity
Queue["SessionQueue"] --> Entity
The Event structure carries entities through the system:
| Field | Type | Purpose |
|---|---|---|
Name |
string |
Human-readable event identifier |
Entity |
*dbt.Entity |
Wrapped OAM asset |
Meta |
interface{} |
Optional metadata (e.g., EmailMeta) |
Dispatcher |
Dispatcher |
Reference for dispatching new events |
Session |
Session |
Associated session context |
Three-Tier Storage Architecture¶
Amass maintains three distinct storage layers, each serving a specific purpose in the data lifecycle:
graph TB
subgraph "Discovery Layer"
Plugins["Discovery Plugins"]
Event["et.Event<br/>with dbt.Entity"]
end
subgraph "Tier 1: Work Queue"
QueueDB["SQLite Queue DB<br/>queue.db"]
Element["Element Table<br/>ID, Type, EntityID, Processed"]
end
subgraph "Tier 2: Session Cache"
CacheDB["SQLite Cache DB<br/>cache.db"]
TempStorage["Temporary Storage<br/>During Enumeration"]
end
subgraph "Tier 3: Graph Database"
GraphDB["Persistent Database"]
SQLite["SQLite<br/>assetdb.db"]
Postgres["PostgreSQL"]
Neo4j["Neo4j"]
end
Plugins --> Event
Event --> QueueDB
QueueDB --> Element
Event --> CacheDB
CacheDB --> TempStorage
CacheDB -->|"Flush on interval"| GraphDB
GraphDB --> SQLite
GraphDB --> Postgres
GraphDB --> Neo4j
style QueueDB fill:#fff5e6
style CacheDB fill:#e6f5ff
style GraphDB fill:#e6ffe6
| Tier | Purpose | Lifetime | Backend | Location |
|---|---|---|---|---|
| Cache | Temporary asset storage, deduplication | Session duration | SQLite | <tmpdir>/cache.db |
| Queue | Work item tracking, processing state | Session duration | SQLite + GORM | <tmpdir>/queue.db |
| Persistent | Long-term asset storage | Permanent | SQLite/Postgres/Neo4j | Configurable |
Tier 1: Work Queue Database¶
The work queue is a SQLite database (queue.db) that tracks which assets need processing. It implements a priority-based FIFO queue for each asset type.
Database Schema:
erDiagram
Element {
uint64 ID PK
time CreatedAt
time UpdatedAt
string Type "Asset type (FQDN, IPAddress, etc.)"
string EntityID "Unique entity identifier"
bool Processed "Processing status"
}
Indexes:
- idx_created_at — Orders by creation time (FIFO)
- idx_etype — Filters by asset type
- idx_entity_id — Ensures uniqueness
- idx_processed — Filters unprocessed items
Key Operations:
| Method | Purpose | SQL Pattern |
|---|---|---|
Has(eid) |
Check if entity is queued | SELECT COUNT(*) WHERE entity_id = ? |
Append(type, eid) |
Add to queue | INSERT INTO elements (type, entity_id, processed) VALUES (?, ?, false) |
Next(type, num) |
Get next N unprocessed | SELECT * WHERE etype = ? AND processed = false ORDER BY created_at LIMIT ? |
Processed(eid) |
Mark as processed | UPDATE elements SET processed = true WHERE entity_id = ? |
Delete(eid) |
Remove from queue | DELETE FROM elements WHERE entity_id = ? |
sequenceDiagram
participant Dispatcher
participant QueueDB
participant Cache
participant Pipeline
Dispatcher->>QueueDB: Next(FQDN, 100)
QueueDB-->>Dispatcher: [entity_id_1, entity_id_2, ...]
loop For each entity_id
Dispatcher->>Cache: FindEntityById(id)
Cache-->>Dispatcher: dbt.Entity
Dispatcher->>Pipeline: Append(Event)
end
Pipeline->>Dispatcher: Processing complete
Dispatcher->>QueueDB: Processed(entity_id)
Tier 2: Session Cache¶
The session cache is a temporary SQLite database (cache.db) created per session. It uses the cache.Cache abstraction from the asset-db library.
Cache Lifecycle:
graph LR
Create["Session Created"]
TmpDir["Temporary Directory<br/>/tmp/session-{UUID}"]
CacheFile["cache.db created"]
Flush["Periodic Flush<br/>Every 1 minute"]
GraphDB["Graph Database"]
Cleanup["Session Terminated<br/>Cache deleted"]
Create --> TmpDir
TmpDir --> CacheFile
CacheFile --> Flush
Flush --> GraphDB
CacheFile --> Cleanup
| Component | Implementation |
|---|---|
| Cache Interface | cache.Cache |
| Backend Repository | SQLite file |
| Flush Interval | 1 minute |
| DSN Options | busy_timeout=30000, journal_mode=WAL |
Tier 3: Persistent Graph Database¶
The persistent graph database is the single source of truth for all discovered assets. Amass supports three database backends:
graph TB
Config["Config.GraphDBs<br/>[]*Database"]
subgraph "SQLite (Default)"
SQLiteDSN["DSN: assetdb.db?_pragma=busy_timeout(30000)&_pragma=journal_mode(WAL)"]
SQLiteFile["File: {outputdir}/assetdb.db"]
end
subgraph "PostgreSQL"
PgDSN["DSN: host={host} port={port} user={user} password={password} dbname={dbname}"]
PgConn["Connection String"]
end
subgraph "Neo4j"
Neo4jDSN["DSN: neo4j+s://{host}:{port}"]
Neo4jBolt["Bolt Protocol"]
end
Config -->|"System: sqlite"| SQLiteDSN
Config -->|"System: postgres"| PgDSN
Config -->|"System: neo4j"| Neo4jDSN
SQLiteDSN --> SQLiteFile
PgDSN --> PgConn
Neo4jDSN --> Neo4jBolt
Environment Variable Support:
| Variable | Purpose | Default |
|---|---|---|
AMASS_DB_USER |
Database username | (required) |
AMASS_DB_PASSWORD |
Database password | (optional) |
AMASS_DB_HOST |
Database host | localhost |
AMASS_DB_PORT |
Database port | 5432 |
AMASS_DB_NAME |
Database name | assetdb |
Asset and Entity Lifecycle¶
Assets flow through multiple stages from initial discovery to persistent storage:
graph TB
subgraph "Stage 1: Discovery"
Plugin["Plugin Handler"]
NewAsset["Create OAM Asset<br/>(e.g., oamdns.FQDN)"]
end
subgraph "Stage 2: Cache Creation"
CacheCreate["cache.CreateAsset(asset)"]
Entity["Returns dbt.Entity<br/>with ID and timestamps"]
end
subgraph "Stage 3: Event Dispatch"
Event["Create et.Event"]
Dispatcher["Dispatcher.DispatchEvent()"]
end
subgraph "Stage 4: Queue Management"
QueueCheck["Queue.Has(entity)?"]
QueueAppend["Queue.Append(entity)"]
QueueElement["SQLite: Insert Element"]
end
subgraph "Stage 5: Pipeline Processing"
PipelineFetch["Dispatcher fills pipeline<br/>from Queue.Next()"]
HandlerExec["Handler.Callback(event)"]
end
subgraph "Stage 6: Persistence"
CacheFlush["Cache flushes<br/>every 1 minute"]
GraphDB["Graph Database<br/>Permanent Storage"]
end
Plugin --> NewAsset
NewAsset --> CacheCreate
CacheCreate --> Entity
Entity --> Event
Event --> Dispatcher
Dispatcher --> QueueCheck
QueueCheck -->|"false"| QueueAppend
QueueAppend --> QueueElement
QueueElement --> PipelineFetch
PipelineFetch --> HandlerExec
HandlerExec -->|"New assets"| NewAsset
CacheCreate --> CacheFlush
CacheFlush --> GraphDB
Stage-by-Stage Breakdown¶
Stage 1 — Plugin Discovery: A plugin handler discovers new information (e.g., DNS lookup finds an IP) and creates an OAM asset object.
Stage 2 — Cache Creation: Calls session.Cache().CreateAsset(oamAsset). Cache wraps the asset in dbt.Entity with a unique ID and timestamp.
Stage 3 — Event Dispatch: Plugin creates et.Event with the entity and calls dispatcher.DispatchEvent(event).
Stage 4 — Queue Management: Dispatcher checks session.Queue().Has(entity) to prevent duplicates. If not queued, calls session.Queue().Append(entity).
Stage 5 — Pipeline Processing: Dispatcher periodically calls Queue.Next(assetType, 100) to fill pipelines. Handlers execute in priority order (1–9). After processing, marks as processed: Queue.Processed(entity).
Stage 6 — Persistence: Cache flushes to the graph database every 1 minute.
Asset Creation from Configuration Scope¶
Initial assets are created from the configuration scope before enumeration begins:
graph LR
ConfigScope["config.Scope"]
Domains["Domains: []string"]
Addresses["Addresses: []net.IP"]
CIDRs["CIDRs: []*net.IPNet"]
ASNs["ASNs: []int"]
FQDN["oamdns.FQDN"]
IPAddr["network.IPAddress"]
Netblock["network.Netblock"]
AS["network.AutonomousSystem"]
Assets["[]*et.Asset"]
ConfigScope --> Domains
ConfigScope --> Addresses
ConfigScope --> CIDRs
ConfigScope --> ASNs
Domains --> FQDN
Addresses --> IPAddr
CIDRs --> Netblock
ASNs --> AS
FQDN --> Assets
IPAddr --> Assets
Netblock --> Assets
AS --> Assets
| Scope Field | OAM Asset Type | Properties |
|---|---|---|
Domains []string |
oamdns.FQDN |
Name: domain |
Addresses []net.IP |
network.IPAddress |
Address: netip.Addr, Type: "IPv4"/"IPv6" |
CIDRs []*net.IPNet |
network.Netblock |
CIDR: netip.Prefix, Type: "IPv4"/"IPv6" |
ASNs []int |
network.AutonomousSystem |
Number: asn |
Session-Specific Storage¶
Each session maintains isolated storage in a temporary directory:
graph TB
Session["Session Object<br/>UUID: {session-id}"]
OutputDir["Output Directory<br/>config.OutputDirectory()"]
TmpDir["Temporary Directory<br/>/tmp/session-{UUID}"]
CacheFile["cache.db<br/>Session Cache"]
QueueFile["queue.db<br/>Work Queue"]
GraphFile["assetdb.db<br/>Graph Database<br/>(SQLite only)"]
Session --> OutputDir
OutputDir --> TmpDir
OutputDir --> GraphFile
TmpDir --> CacheFile
TmpDir --> QueueFile
Cleanup["Session Terminated"]
Cleanup -->|"os.RemoveAll(tmpdir)"| TmpDir
Cleanup -->|"Kept permanently"| GraphFile
Directory structure:
{OutputDir}/
├── assetdb.db # Persistent graph database (SQLite)
└── session-{UUID}/ # Temporary session directory
├── cache.db # Session cache (deleted on exit)
└── queue.db # Work queue (deleted on exit)
Asset Types and Properties¶
Asset Type Hierarchy¶
graph TD
Asset["oam.Asset Interface<br/>AssetType() string<br/>Key() string"]
subgraph "DNS Assets (oamdns)"
FQDN["FQDN<br/>Name: string"]
end
subgraph "Network Assets (oamnet)"
IPAddress["IPAddress<br/>Address: netip.Addr<br/>Type: string"]
Netblock["Netblock<br/>CIDR: netip.Prefix<br/>Type: string"]
AutonomousSystem["AutonomousSystem<br/>Number: int"]
end
subgraph "Organization Assets"
Organization["Organization<br/>Name: string<br/>Industry: string"]
end
subgraph "Service Assets"
Service["Service<br/>Port: int<br/>Protocol: string"]
end
subgraph "Contact Assets"
Contact["Contact<br/>Type: string<br/>Value: string"]
Location["Location<br/>Address: string"]
end
Asset --> FQDN
Asset --> IPAddress
Asset --> Netblock
Asset --> AutonomousSystem
Asset --> Organization
Asset --> Service
Asset --> Contact
Asset --> Location
Property Attachment Model¶
graph LR
Entity["dbt.Entity<br/>(Asset Wrapper)<br/>ID, Asset, CreatedAt"]
subgraph "Entity Properties"
Prop1["DNSRecordProperty<br/>RRType, TTL, Data"]
Prop2["SourceProperty<br/>Source, Confidence"]
Prop3["Custom Properties"]
end
Entity -->|"CreateEntityProperty()"| Prop1
Entity -->|"CreateEntityProperty()"| Prop2
Entity -->|"CreateEntityProperty()"| Prop3
Edge["dbt.Edge<br/>(Relationship)<br/>FromEntity, ToEntity"]
subgraph "Edge Properties"
EdgeProp1["SourceProperty<br/>Source, Confidence"]
EdgeProp2["Custom Properties"]
end
Edge -->|"CreateEdgeProperty()"| EdgeProp1
Edge -->|"CreateEdgeProperty()"| EdgeProp2
DNSRecordProperty¶
The DNSRecordProperty attaches DNS record information to FQDN entities.
| Field | Type | Description |
|---|---|---|
PropertyName |
string |
Always "dns_record" |
Header.RRType |
int |
DNS record type (1=A, 5=CNAME, 15=MX, 16=TXT, 28=AAAA…) |
Header.Class |
int |
DNS class (typically 1 for IN) |
Header.TTL |
int |
Time to live in seconds |
Data |
string |
Record-specific data |
_, err := session.Cache().CreateEntityProperty(fqdn, &oamdns.DNSRecordProperty{
PropertyName: "dns_record",
Header: oamdns.RRHeader{
RRType: int(dns.TypeTXT),
Class: int(record.Header().Class),
TTL: int(record.Header().Ttl),
},
Data: txtValue,
})
Asset to Entity Conversion¶
sequenceDiagram
participant Plugin as "DNS Plugin"
participant Cache as "session.Cache()"
participant DB as "Graph Database"
Plugin->>Plugin: Create OAM Asset<br/>e.g., oamdns.FQDN{Name: "example.com"}
Plugin->>Cache: CreateAsset(asset)
Cache->>DB: Check if asset exists
alt Asset exists
DB-->>Cache: Return existing entity
else New asset
DB->>DB: Create new entity<br/>Assign ID, timestamp
DB-->>Cache: Return new entity
end
Cache-->>Plugin: Return dbt.Entity
Plugin->>Cache: CreateEntityProperty(entity, property)
Cache->>DB: Attach property to entity
DB-->>Cache: Success
Plugin->>Plugin: Dispatch event with entity
DNS Plugin Asset Flow¶
graph TD
Start["Event with FQDN Entity"]
Start --> Extract["Extract Asset from Entity<br/>fqdn := entity.Asset.(*oamdns.FQDN)"]
Extract --> Query["DNS Query<br/>PerformQuery(fqdn.Name, TypeA)"]
Query --> CreateIP["Create IPAddress Asset<br/>oamnet.IPAddress{Address, Type}"]
CreateIP --> StoreIP["Store as Entity<br/>session.Cache().CreateAsset(ip)"]
StoreIP --> CreateEdge["Create DNS Record Edge<br/>oamdns.BasicDNSRelation"]
CreateEdge --> AddProp["Add SourceProperty to Edge"]
AddProp --> Dispatch["Dispatch Event with IP Entity"]
Asset Type Summary¶
| Asset Type | Package | Key Structure | Primary Use Case |
|---|---|---|---|
FQDN |
oamdns |
Domain name | DNS enumeration, subdomain discovery |
IPAddress |
oamnet |
IP address string | Network infrastructure mapping |
Netblock |
oamnet |
CIDR notation | Network range definition |
AutonomousSystem |
oamnet |
ASN | BGP/network ownership |
Organization |
oam |
Organization name | Entity attribution and enrichment |
Service |
oam |
Service identifier | Active service discovery |
Contact |
oam |
Contact value | WHOIS/organization contacts |
Location |
oam |
Address | Physical location data |
Relationships and Edges¶
Edge Structure¶
Edges are represented by the dbt.Edge struct with three required components:
- Relation — An object implementing
oam.Relationthat defines the relationship type - FromEntity — The source entity of the directed edge
- ToEntity — The destination entity of the directed edge
Edge Creation Pattern¶
graph LR
Plugin["Plugin Handler"]
CreateEdge["cache.CreateEdge()"]
EdgeProperty["cache.CreateEdgeProperty()"]
SourceProp["SourceProperty"]
Plugin --> CreateEdge
CreateEdge --> EdgeProperty
EdgeProperty --> SourceProp
SourceProp -.->|"Tracks confidence<br/>and origin"| Edge["dbt.Edge"]
CreateEdge -.->|"Returns"| Edge
edge, err := e.Session.Cache().CreateEdge(&dbt.Edge{
Relation: &oamdns.BasicDNSRelation{
Name: "dns_record",
Header: oamdns.RRHeader{RRType: 1, Class: 1, TTL: 300},
},
FromEntity: fqdn,
ToEntity: ipEntity,
})
if err == nil && edge != nil {
e.Session.Cache().CreateEdgeProperty(edge, &general.SourceProperty{
Source: "DNS",
Confidence: 100,
})
}
DNS Relations¶
graph LR
FQDN1["FQDN Entity<br/>example.com"]
FQDN2["FQDN Entity<br/>www.example.com"]
IP["IPAddress Entity<br/>192.0.2.1"]
CNAME["FQDN Entity<br/>cdn.provider.com"]
FQDN1 -->|"BasicDNSRelation<br/>dns_record<br/>RRType: A(1)"| IP
FQDN2 -->|"BasicDNSRelation<br/>dns_record<br/>RRType: CNAME(5)"| CNAME
graph LR
FQDN["FQDN Entity<br/>example.com"]
MX1["FQDN Entity<br/>mail1.example.com"]
MX2["FQDN Entity<br/>mail2.example.com"]
FQDN -->|"PrefDNSRelation<br/>dns_record<br/>RRType: MX(15)<br/>Preference: 10"| MX1
FQDN -->|"PrefDNSRelation<br/>dns_record<br/>RRType: MX(15)<br/>Preference: 20"| MX2
Identity Relations¶
graph LR
Org["Organization Entity<br/>Google LLC"]
LEI["Identifier Entity<br/>LEI:549300GZSUFD98NHH337"]
Email["Identifier Entity<br/>email:admin@example.com"]
AviID["Identifier Entity<br/>aviato_company:abc123"]
Org -->|"SimpleRelation<br/>id"| LEI
Org -->|"SimpleRelation<br/>id"| Email
Org -->|"SimpleRelation<br/>id"| AviID
Organizational Relations¶
graph TB
Parent["Organization<br/>Alphabet Inc."]
Child1["Organization<br/>Google LLC"]
Child2["Organization<br/>Waymo LLC"]
Parent -->|"SimpleRelation<br/>subsidiary"| Child1
Parent -->|"SimpleRelation<br/>subsidiary"| Child2
Location Relations¶
graph TB
Org["Organization<br/>ACME Corp"]
Legal["Location<br/>123 Legal St<br/>New York, NY"]
HQ["Location<br/>456 HQ Ave<br/>San Francisco, CA"]
Other["Location<br/>789 Office Rd<br/>Austin, TX"]
Org -->|"SimpleRelation<br/>legal_address"| Legal
Org -->|"SimpleRelation<br/>hq_address"| HQ
Org -->|"SimpleRelation<br/>location"| Other
Network Relations¶
graph LR
FQDN["FQDN Entity<br/>web.example.com"]
IP["IPAddress Entity<br/>192.0.2.1"]
Service1["Service Entity<br/>HTTP on :443"]
Service2["Service Entity<br/>SSH on :22"]
FQDN -->|"PortRelation<br/>port: 443<br/>protocol: tcp"| Service1
IP -->|"PortRelation<br/>port: 22<br/>protocol: tcp"| Service2
graph LR
IP["IPAddress Entity<br/>192.0.2.1"]
PTR["FQDN Entity<br/>1.2.0.192.in-addr.arpa"]
Target["FQDN Entity<br/>mail.example.com"]
IP -->|"SimpleRelation<br/>ptr_record"| PTR
PTR -->|"BasicDNSRelation<br/>dns_record<br/>RRType: PTR(12)"| Target
Domain Hierarchy (node relation)¶
graph TB
Apex["FQDN Entity<br/>example.com"]
Sub1["FQDN Entity<br/>www.example.com"]
Sub2["FQDN Entity<br/>mail.example.com"]
Sub3["FQDN Entity<br/>api.subdomain.example.com"]
Sub4["FQDN Entity<br/>subdomain.example.com"]
Apex -->|"SimpleRelation<br/>node"| Sub1
Apex -->|"SimpleRelation<br/>node"| Sub2
Apex -->|"SimpleRelation<br/>node"| Sub4
Sub4 -->|"SimpleRelation<br/>node"| Sub3
Contact Relations¶
graph TB
DomRec["DomainRecord<br/>example.com"]
Contact["ContactRecord<br/>Registrant Info"]
Person["Person<br/>John Doe"]
Org["Organization<br/>Example Corp"]
Loc["Location<br/>123 Main St"]
Email["Identifier<br/>email:admin@example.com"]
Phone["Phone<br/>+1-555-1234"]
DomRec -->|"SimpleRelation<br/>registrant_contact"| Contact
Contact -->|"SimpleRelation<br/>person"| Person
Contact -->|"SimpleRelation<br/>organization"| Org
Contact -->|"SimpleRelation<br/>location"| Loc
Contact -->|"SimpleRelation<br/>id"| Email
Contact -->|"SimpleRelation<br/>phone"| Phone
Contact relation types include: registrant_contact, admin_contact, technical_contact, billing_contact, abuse_contact, subject_contact, issuer_contact.
Certificate Relations¶
graph TB
Cert["TLSCertificate<br/>Serial: ABC123"]
CN["FQDN Entity<br/>*.example.com"]
SAN1["FQDN Entity<br/>www.example.com"]
SAN2["FQDN Entity<br/>api.example.com"]
IP["IPAddress Entity<br/>192.0.2.1"]
Email["Identifier<br/>email:admin@example.com"]
URL["URL Entity<br/>http://ocsp.ca.com"]
Service["Service Entity<br/>HTTPS on :443"]
Cert -->|"SimpleRelation<br/>common_name"| CN
Cert -->|"SimpleRelation<br/>san_dns_name"| SAN1
Cert -->|"SimpleRelation<br/>san_dns_name"| SAN2
Cert -->|"SimpleRelation<br/>san_ip_address"| IP
Cert -->|"SimpleRelation<br/>san_email_address"| Email
Cert -->|"SimpleRelation<br/>ocsp_server"| URL
Service -->|"SimpleRelation<br/>certificate"| Cert
Financial Relations¶
graph LR
Investor["Organization<br/>VC Fund"]
SeedAcct["Account<br/>Seed Round A"]
Transfer["FundsTransfer<br/>$5M USD"]
OrgAcct["Account<br/>Startup Checking"]
Org["Organization<br/>Startup Inc."]
Investor -->|"SimpleRelation<br/>account"| SeedAcct
Transfer -->|"SimpleRelation<br/>sender"| SeedAcct
Transfer -->|"SimpleRelation<br/>recipient"| OrgAcct
Org -->|"SimpleRelation<br/>account"| OrgAcct
Domain Registration Relations¶
graph TB
DomRec["DomainRecord<br/>example.com"]
NS1["FQDN<br/>ns1.nameserver.com"]
NS2["FQDN<br/>ns2.nameserver.com"]
WHOIS["FQDN<br/>whois.registry.com"]
DomRec -->|"SimpleRelation<br/>name_server"| NS1
DomRec -->|"SimpleRelation<br/>name_server"| NS2
DomRec -->|"SimpleRelation<br/>whois_server"| WHOIS
Source Attribution¶
Every edge should have a SourceProperty attached to track which plugin discovered the relationship and with what confidence:
type SourceProperty struct {
Source: string // Plugin/source name
Confidence: int // 0-100 confidence score
}
graph TD
Handler["Plugin Handler"]
CreateAssets["Create Assets"]
CreateEdge["Create Edge"]
AttachSource["Attach SourceProperty"]
Handler --> CreateAssets
CreateAssets --> CreateEdge
CreateEdge --> AttachSource
AttachSource -.->|"Tracks:<br/>• Plugin name<br/>• Confidence<br/>• Timestamp"| EdgeTags["Edge Tags"]