info object of an extension definition schema using static Markdown or dynamic Go templates.
This page covers custom section structure, Markdown and template validation, and dynamic content options.
Custom Entity Panel content
Use theinfo object to define custom Entity Panel sections for node kinds and relationship kinds. When a user selects a node or relationship in Explore, BloodHound renders an Entity Panel with the specified accordion sections as defined by the info entries.
Each info object is a map of section identifiers to rendered sections. Use stable identifiers for the keys so future schema updates can modify the same section without changing its identity.
string
Stable section identifier. Must match
^[a-z0-9_-]{1,128}$, which allows lowercase letters, numbers, hyphens, and underscores.string
Section title displayed in the Entity Panel accordion header.
integer
Positive integer (
1 or higher) that controls the order of extension-defined sections. Lower values render first. Use 1 for the first extension-defined section.string
Single-line JSON string containing the Markdown rendered in the section body. Encode line breaks as escaped
\n sequences. Required when markdown is present. You can use static Markdown or a Go template that renders values for the selected entity.0. This section lists all properties for the selected node or relationship.
BloodHound renders additional info entries for the selected node’s primary kind or the selected relationship’s kind after Object Information. BloodHound orders those extension-defined sections by position, then title.
If no info entries are defined for the selected kind, BloodHound still renders Object Information, but does not render additional extension-defined Entity Panel sections.
Markdown validation
When you upload an extension definition schema, BloodHound validates each Markdown field to ensure it does not contain unsafe or disallowed HTML.Validation only checks the content; BloodHound stores your original Markdown without modifying it.
Supported Markdown
BloodHound supports CommonMark plus the following GitHub Flavored Markdown extensions:- Tables
- Strikethrough
- Task lists
- Autolinks
- Fenced code blocks (with an alphanumeric language hint)
Rejected content
BloodHound rejects the upload when a Markdown field contains unsafe or disallowed HTML, including:<script>tags and other executable content- Inline event-handler attributes, such as
onclick javascript:links<iframe>,<style>, and similar elements
Dynamic Entity Panel content
Most Entity Panel content can be ordinary Markdown. When a panel should adapt to the selected node or relationship, use a template expression to insert values or describe relationship endpoints. This guide is organized by authoring goal:- Read a value: Basic templates
- Transform or reuse a value: Pipelines and variables
- Include or omit content: Control flow
Template expressions
The simplest template expression evaluates a value and inserts the result into the Markdown. Expressions use Gotext/template syntax and are enclosed in {{ and }}:
name property is Alice, the rendered content is:
Basic templates
Template expressions evaluate values available to the template and insert their results into Markdown. Those values may come from the current graph context, such as.Properties, .Source, or .Target, or from template variables declared with :=. The surrounding Markdown remains literal, while the template determines which values are evaluated and where their results appear.
- Graph context values come from the selected node or relationship and are accessed from
.. - Template variables are local names created with
:=, such as$nameor$value. They store a value so the template can reuse it.
Graph context
The graph context is the entity data made available to the panel. Its shape depends on whether the panel is rendered for a node or a relationship.Node context
For a node panel, the root context represents the selected node:
Template example:
Relationship context
For a relationship panel, the root context represents the relationship and includes source and target node contexts:
Example:
Direct Property References and get
Use a direct property reference when the key is a normal identifier, such as .Properties.displayName.
Use get when you need a string-based map lookup—for example, when a key contains punctuation or spaces, or when the key is stored in another template variable:
get only looks up a key in the .Properties map; it does not retrieve additional graph data or make absent properties available.
Function syntax
Go template functions use space-separated call syntax: the function name comes first, followed by its arguments. Parentheses can group an argument and nest a function call, but they are not written as conventional function-call syntax::= when it needs to be reused.
Pipelines
Go template pipelines use| to pass the result of one command to the next command as its final argument:
replace receives its fixed arguments (" " and "-") before the piped property value. If the piped value belongs in another position, use a direct function call and pipe its result instead. For example, get expects the map before the key:
Whitespace trimming
Hyphens next to the template brackets are optional. They trim whitespace around an action:{{-trims whitespace immediately before the action.}}trims whitespace immediately after the action.{{- ... -}}trims whitespace on both sides.
isTierZero is false, the rendered content contains an empty line:
isTierZero is false, the rendered content is:
Access: admin, while:
Access:admin.
Template variables
Template variables are local names that start with$ and are assigned with :=.
A variable declared in the current template body remains available through the end of that template execution. A variable declared inside an if, range, or with block is available only within that block. Each markdown.content value is rendered independently, so variables do not carry between separate Entity Panel objects.
Reusable variables
Create a local template value by writing a$-prefixed identifier first, followed by := and the expression that supplies its value: $identifier := expression. Reuse that value later by referencing the same identifier, such as $edgeType. In the example below, each value is assigned once and reused in multiple rendered sentences or list items.
Control flow
Theif, else, end, range, and with elements are Go template actions (not helper functions) that control if and how parts of the Markdown content are displayed.
Required versus optional syntax
The following structural elements are required or optional as follows:ifstarts a conditional block.endcloses anif,range, orwithblock.elseis optional and provides the alternate branch of anifblock.else ifis optional and adds another conditional branch before the finalelseorend.rangestarts an iteration block.withstarts a block that changes the current context when a value is present.- Hyphens next to the brackets are optional whitespace controls. They are not required for any action.
if action requires one or more arguments that evaluate to boolean values. Use conditional checks to produce those values. The following example shows an if action without an else or else if branch:
else action does not take arguments; it renders the alternate branch when the if condition is false. See Conditional content below for an example.
An else if action accepts its own arguments (like if) and evaluates them only when the preceding condition is false. Use else if to add another conditional branch before an optional else action.
Conditional content
A conditional can wrap a sentence, paragraph, list, code block, or complete Markdown subsection. This is useful when the same guidance needs different explanatory or action-oriented copy depending on the selected entity’s state. For example, security guidance for an entity could present different Kerberos-delegation options for service accounts and non-service accounts. Protected Users might be preferred for non-service accounts, while service accounts may not be compatible with Protected Users; in that case, mark the account as sensitive instead. The property name below is illustrative; use the property exposed by the extension’s entity data. The following example shows anif/else pair:
else when the reader needs alternate wording. Use with when a block should appear only when a value is present; inside the block, . becomes that value. Use range to render a collection, such as a list of tags.
Supported functions
Functions are separate from Go template actions. The Entity Panel function map is a curated Sprig-based allowlist.
Template actions:
The function map excludes functions that are unsafe, expensive, or inappropriate for Entity Panel authoring, including regular-expression, cryptographic, certificate-generation, random-number, URL-parsing, and URL-joining helpers. The supported-function reference is the source of truth for the target release.
Errors and testing
If a template cannot be parsed or executed, the response retains the original unexecuted template string and includes a template error describing the failure. Schema validation should catch many invalid syntax errors, but not all errors are known prior to runtime resolution. Common causes include:- Misspelled context fields or properties
- Unsupported functions
- Invalid template syntax
- Missing values without fallback logic
- Values passed to a helper with an unexpected type
- Start with static Markdown.
- Add one value expression.
- Add a fallback with
default. - Add a pipeline or reusable variable.
- Add control flow and test every branch.
- Inspect rendered Markdown, including whitespace.