Documentation Index
Fetch the complete documentation index at: https://bloodhound.specterops.io/llms.txt
Use this file to discover all available pages before exploring further.
Use this page to validate structure before ingestion. For a full data payload example, see Graph Data.
start endpoint, an end endpoint, and a kind that describes the relationship type.
An object that defines how to match the starting node of the edge. See Endpoint Matching.
An object that defines how to match the ending node of the edge. See Endpoint Matching.
A string that describes the relationship type.
-
A descriptive name that identifies the edge kind and does not overlap with built-in edge kinds.
Consider using a prefix related to your data source or environment separated from the name by an underscore. For example,
Okta_ResetPassword. For structured graphs, a prefix that matches the extension’snamespaceis required. -
Must match the regex pattern
^[A-Za-z0-9_]+$, which means edge kinds can only contain uppercase letters, lowercase letters, numbers, and underscores. Spaces, dashes, backticks, and other special characters are not allowed in edge kinds. PascalCase is recommended for readability and consistency. Neo4j Cypher allows many special characters in symbolic names when the name is enclosed in backticks. BloodHound OpenGraph ingest is more restrictive: edgekindvalues must match^[A-Za-z0-9_]+$, so upload validation rejects backtick-escaped names, spaces, dashes, and other special characters.
A key-value map of custom edge properties. Values must be strings, numbers, booleans, or arrays of primitives. Nested objects and arrays of objects are not allowed.
Unless otherwise noted, examples below show edge objects only.
Endpoint Matching
Edges in OpenGraph data define relationships between nodes using astart endpoint object and an end endpoint object. You can control how BloodHound resolves each endpoint using one of three matching strategies in the match_by field.
idfor direct node identifier matchingpropertyfor node property matchingnamefor legacy node name matching (deprecated)
Use identifier matching when possible. Property matching is more flexible, but it is slower and should be used only when you cannot match by node ID.
Match by Identifier
This is the default and most common method. It resolves an endpoint by unique nodeid.
Linking a specific user to a server using their unique IDs
Starting endpoint definition for the edge.
Strategy for matching the starting node. If omitted, defaults to
id for unique identifier matching.String containing the specific ID of the starting node.
Ending endpoint definition for the edge.
Strategy for matching the ending node. If omitted, defaults to
id for unique identifier matching.String containing the specific ID of the ending node.
Optional kind filter used in
start to constrain the lookup to a specific node kind.For example, setting kind: "User" ensures that even if a name exists across multiple entity types, only the one classified as a User is selected.Optional kind filter used in
end to constrain the lookup to a specific node kind.For example, setting kind: "Server" limits endpoint resolution to nodes classified as Server.Not used in this mode. If provided alongside
start.match_by: "id", validation fails.Not used in this mode. If provided alongside
end.match_by: "id", validation fails.Match by Property
Use this strategy when you do not know the unique ID of the target node but can identify it using one or more knownproperties (for example, username, email address, hostname, or custom property). This method allows for dynamic resolution based on data available at the time of ingestion.
To use this strategy, set the match_by property to property.
Linking a user to a server by matching the user's username property and the server's hostname property
Starting endpoint definition for the edge.
Strategy for matching the starting node. Set to
property to match against one or more of the starting node’s property values.Array of matchers used to find the starting node. BloodHound attempts to find a node that satisfies all matchers.At least one matcher is required, but you can provide multiple matchers in the array. The system will attempt to find a node that satisfies all conditions simultaneously.
Optional kind filter used to narrow node resolution for the starting node.
Ending endpoint definition for the edge.
Strategy for matching the ending node. Set to
property to match against one or more of the ending node’s property values.Array of matchers used to find the ending node.At least one matcher is required, but you can provide multiple matchers in the array. The system will attempt to find a node that satisfies all conditions simultaneously.
Not used in this mode. Providing
start.value when start.match_by is property causes validation errors.Not used in this mode. Providing
end.value when end.match_by is property causes validation errors.Name of the node property to check.
Matching operator.
equals is currently the only supported value.Expected value for the property matcher.
Match by Name (deprecated)
Use this legacy strategy to resolve an endpoint by aname string. This strategy predates Match by Property and uses the value field directly, similar to Match by Identifier. It is retained for backward compatibility.
To use this strategy, set the match_by property to name and provide the name string in value. Combine with an optional kind filter to disambiguate nodes that share names across kinds.
Linking a user to a server by their names
name match to a property match against the name property. The equivalent payload using Match by Property:
Starting endpoint definition for the edge.
Strategy for matching the starting node. Set to
name to match against a name string.Name string used to look up the starting node.
Optional kind filter used to constrain the lookup to a specific node kind. Recommended when names overlap across kinds.
Ending endpoint definition for the edge.
Strategy for matching the ending node. Set to
name to match against a name string.Name string used to look up the ending node.
Optional kind filter used to constrain the lookup to a specific node kind. Recommended when names overlap across kinds.
Not used in this mode. Providing
property_matchers when start.match_by is name causes validation errors.Not used in this mode. Providing
property_matchers when end.match_by is name causes validation errors.Post-processing
Post-processing in BloodHound runs during the analysis phase. During this phase, BloodHound generates specific edges to enrich the graph and reflect the evaluated graph state. After ingest completes, BloodHound builds a complete graph, deletes existing post-processed edges, and regenerates them. As a result, post-processed edge kinds that you add directly in OpenGraph payloads do not persist.Show post-processed edges
Show post-processed edges
BloodHound creates the following edges during post-processing:
ADCSESC1ADCSESC3ADCSESC4ADCSESC6aADCSESC6bADCSESC9aADCSESC9bADCSESC10aADCSESC10bADCSESC13AddMemberAdminToAZAddOwnerAZMGAddMemberAZMGAddOwnerAZMGAddSecretAZMGGrantAppRolesAZMGGrantRoleAZRoleApproverCanPSRemoteCanRDPCoerceAndRelayNTLMToADCSCoerceAndRelayNTLMToLDAPCoerceAndRelayNTLMToLDAPSCoerceAndRelayNTLMToSMBDCSyncEnrollOnBehalfOfEnterpriseCAForExecuteDCOMExtendedByPolicyGoldenCertHasTrustKeysIssuedSignedByOwnsOwnsLimitedRightsProtectAdminGroupsSyncLAPSPasswordSyncedToADUserSyncedToEntraUserTrustedForNTAuthWriteOwnerWriteOwnerLimitedRights
AdminTo edge directly in your OpenGraph payload, BloodHound removes it during post-processing and the edge does not persist in the final graph as expected. Instead of adding AdminTo edges directly, include the supporting edges that cause the post-processor to generate the AdminTo edge. The common pattern that triggers the creation of the AdminTo edge is:
See the following example OpenGraph payload that produces the effect:
Schema
Use the schema below as the source of truth for validation requirements. You can also download the same schema as a file: opengraph-edge.json.Troubleshooting
-
Upload fails on edge kind pattern: Ensure
kindmatches^[A-Za-z0-9_]+$. -
Endpoint validation fails: Use either
valueforid/namematching, orproperty_matchersforpropertymatching, not both. - Expected edge disappears after ingest: Check whether it is a post-processed edge kind.