I have no data yet. It is a capital mistake to theorize before one has data. Insensibly one begins to twist facts to suit theories, instead of theories to suit facts.
The plugin framework for OSINTBuddy, a graph-based OSINT platform for recon, OSINT investigations, link analysis, and more. Offline. Local-first workflows. No cloud dependency.
OSINTBuddy's plugin system enables you to define entities (nodes in the graph) and transforms (operations that create new entities from existing ones). The framework provides:
- Entity definitions with rich metadata, icons, colors, and form elements
- Transform decorators with dependency management and version targeting
- Result types for subgraphs, custom edges, and file attachments
- Field types for semantic type-based transform matching
- Settings framework for persistent configuration
- CLI tools for development and integration
pip install osintbuddy[all]For development:
git clone https://github.com/osintbuddy/plugins.git
cd plugins/
pip install -e ".[dev]"from osintbuddy import Plugin
from osintbuddy.elements import TextInput, CopyText
from osintbuddy.types import FieldType
class EmailEntity(Plugin):
version = "1.0.0"
label = "Email"
icon = "mail"
color = "#3B82F6"
category = "Identity"
elements = [
TextInput(label="Email", icon="mail", field_type=FieldType.EMAIL),
CopyText(label="Domain"),
]from osintbuddy import transform, Entity, Edge
@transform(
target="email@>=1.0.0",
label="Extract Domain",
icon="world",
)
async def extract_domain(entity):
email = entity.email
domain = email.split("@")[1] if "@" in email else None
if domain:
return Entity(
data=DomainEntity.blueprint(domain=domain),
edge=Edge(label="has domain"),
)ob run -T '{"label": "email", "version": "1.0.0", "transform": "extract_domain", "data": {"email": "user@example.com"}}'| Guide | Description |
|---|---|
| Getting Started | Installation, project setup, and first plugin |
| Plugins & Entities | Defining entities with the Plugin class |
| Transforms | Creating transforms with the @transform decorator |
| Elements | Input and display elements for entity forms |
| Field Types | Semantic types for fields and type-based matching |
| Settings | Transform configuration and persistence |
| CLI Reference | Command-line interface documentation |
| API Reference | Complete API documentation |
Every node type in the graph is defined as a Plugin subclass. Plugins are automatically registered when defined:
class IPAddress(Plugin):
version = "1.0.0"
label = "IP Address"
elements = [TextInput(label="IP", field_type=FieldType.IP_ADDRESS)]Transforms operate on entities to produce new entities. They target specific entity versions:
@transform(target="ip_address@>=1.0.0", label="GeoIP Lookup", deps=["geoip2"])
async def geoip_lookup(entity):
# Transform logic
return Entity(data=Location.blueprint(city="..."))Transforms return Entity, Edge, File, or Subgraph objects:
return Entity(
data=TargetEntity.blueprint(field="value"),
edge=Edge(label="discovered", color="#22C55E"),
files=[File(path="/tmp/report.pdf")],
)For plugin development and registry submissions, organize your code as:
my-plugins-repo/
├── entities/
│ ├── email.py
│ ├── domain.py
│ └── ip_address.py
└── transforms/
├── email_transforms.py
├── domain_transforms.py
├── network_traceroute_transform.py
└── network_transforms.py
Load plugins via:
from osintbuddy import load_plugins_fs
load_plugins_fs("/path/to/my-plugins", "my_plugins")# List entities and transforms
ob ls entities
ob ls transforms -L email
# Run a transform
ob transform '{"label": "email", "version": "1.0.0", "transform": "to_domain", "data": {...}}'
# Get entity blueprints
ob blueprints -L email
# Compile JSON entity to Python
ob compile entity.json -O entity.py- Python 3.12+
MIT License, see LICENSE for details.