This guide walks you through installing OSINTBuddy, setting up a plugin project, and creating your first entity and transform.
Python 3.12+pip
pip install osintbuddygit clone https://github.com/osintbuddy/plugins.git
cd plugins/
pip install -e ".[dev,all]"ob --helpYou should see the available CLI commands.
Create a plugin project with the following structure:
my-osint-plugins/
├── entities/
│ ├── __init__.py
│ ├── email.py
│ ├── domain.py
│ └── person.py
├── transforms/
│ ├── __init__.py
│ ├── email_transforms.py
│ └── domain_transforms.py
└── manifest.json
The framework expects:
- entities/: Plugin class definitions (one per entity type)
- transforms/: Transform functions decorated with
@transform
Create entities/email.py:
from osintbuddy import Plugin
from osintbuddy.elements import TextInput, CopyText
from osintbuddy.types import FieldType
class EmailEntity(Plugin):
"""An email address entity for OSINT investigations."""
# Required: Semantic version
version = "1.0.0"
# Display metadata
label = "Email"
description = "An email address to investigate"
icon = "mail"
color = "#3B82F6"
# Organization
category = "Identity"
tags = ["email", "identity", "contact"]
# Form definition
elements = [
TextInput(label="Email", icon="mail", field_type=FieldType.EMAIL),
CopyText(label="Domain"),
CopyText(label="Username"),
]| Attribute | Required | Description |
|---|---|---|
version |
Yes | Semantic version (e.g., "1.0.0") |
label |
Yes | Display name in the UI |
elements |
Yes | Form fields for the entity |
icon |
No | Icon identifier (default: "atom-2") |
color |
No | Hex color (default: "#145070") |
category |
No | UI grouping category/categories |
description |
No | Long description |
tags |
No | Searchable tags |
show_in_ui |
No | Whether to show in entity picker (default: true) |
Create transforms/email_transforms.py:
from osintbuddy import transform, Entity, Edge, Registry
from entities.email import EmailEntity
@transform(
target="email@>=1.0.0",
label="Extract Domain",
icon="world",
)
async def extract_domain(entity):
DomainEntity = await Registry.get_entity('domain@1.0.0')
"""Extract the domain from an email address."""
email = entity.email # Access fields via snake_case attribute
if not email or "@" not in email:
return None # Return None if no result
parts = email.split("@")
domain = parts[1]
username = parts[0]
return Entity(
data=DomainEntity.blueprint(domain=domain),
edge=Edge(label="has domain"),
)- target: Entity ID and version spec (e.g.,
"email@>=1.0.0") - label: Display name in the context menu
- async: All transforms must be async functions
- entity: A
TransformPayloadwith field access via snake_case attributes
Use the CLI with the -P flag:
ob entities -P /path/to/my-osint-pluginsIf your plugin includes local JSON or text files (like dropdown options), load them relative to the plugin module:
from osintbuddy import Plugin, read_resource_json
from osintbuddy.elements import DropdownInput
options = read_resource_json(__file__, "options.json", default=[])
class ExamplePlugin(Plugin):
elements = [
DropdownInput(label="Options", options=options),
]ob transform '{
"label": "email",
"version": "1.0.0",
"transform": "extract_domain",
"data": {
"email": "user@example.com"
}
}'Transforms can return various result types:
return Entity(
data=TargetEntity.blueprint(field="value"),
edge=Edge(label="relationship"),
)return [
Entity(data=Entity1.blueprint(...)),
Entity(data=Entity2.blueprint(...)),
]return Entity(
data=Report.blueprint(title="Analysis"),
files=[File(path="/tmp/report.pdf", label="PDF Report")],
)return Subgraph(
entities=[
Entity(data=Node1.blueprint(...)),
Entity(data=Node2.blueprint(...)),
],
edges=[
("node1_id", "node2_id", Edge(label="connects")),
],
)Use the structured error types:
from osintbuddy import PluginError, NetworkError, RateLimitError
@transform(target="domain@>=1.0.0", label="DNS Lookup")
async def dns_lookup(entity):
try:
result = await perform_dns_lookup(entity.domain)
return Entity(data=DNSRecord.blueprint(**result))
except ConnectionError:
raise NetworkError("Failed to connect to DNS server")
except TooManyRequests:
raise RateLimitError("Rate limited by DNS provider")- Plugins & Entities - Deep dive into entity definitions
- Transforms - Advanced transform patterns
- Elements - All available form elements
- Field Types - Type-based matching
- Settings - Configuration and persistence
- CLI Reference - Complete CLI documentation