Skip to content

Latest commit

 

History

History
248 lines (187 loc) · 5.86 KB

File metadata and controls

248 lines (187 loc) · 5.86 KB

Getting Started

This guide walks you through installing OSINTBuddy, setting up a plugin project, and creating your first entity and transform.

Prerequisites

  • Python 3.12+
  • pip

Installation from PyPI

pip install osintbuddy

From Source (Development)

git clone https://github.com/osintbuddy/plugins.git
cd plugins/
pip install -e ".[dev,all]"

Verify Installation

ob --help

You should see the available CLI commands.

Project Structure

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

Your First Entity

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"),
    ]

Key Attributes

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)

Your First Transform

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"),
    )

Transform Basics

  • 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 TransformPayload with field access via snake_case attributes

Use the CLI with the -P flag:

ob entities -P /path/to/my-osint-plugins

Loading Plugin Resources

If 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),
    ]

Running Transforms

ob transform '{
  "label": "email",
  "version": "1.0.0",
  "transform": "extract_domain",
  "data": {
    "email": "user@example.com"
  }
}'

Returning Results

Transforms can return various result types:

Single Entity

return Entity(
    data=TargetEntity.blueprint(field="value"),
    edge=Edge(label="relationship"),
)

Multiple Entities

return [
    Entity(data=Entity1.blueprint(...)),
    Entity(data=Entity2.blueprint(...)),
]

With Files

return Entity(
    data=Report.blueprint(title="Analysis"),
    files=[File(path="/tmp/report.pdf", label="PDF Report")],
)

Subgraph

return Subgraph(
    entities=[
        Entity(data=Node1.blueprint(...)),
        Entity(data=Node2.blueprint(...)),
    ],
    edges=[
        ("node1_id", "node2_id", Edge(label="connects")),
    ],
)

Error Handling

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")

Next Steps