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.
πΌ Click here to watch the OMOIKA video demo.
2026-07-02_15-13-30.mp4
Welcome to the OMOIKA project where you can connect, combine, and get insight from unstructured and public data as results that can be explored step-by-step. An easy-to-use plugin system allows any developer to quickly integrate new data sources so you can focus on discovering, interacting, and visualizing what's important to you
This is the plugin framework for OMOIKA, a graph-based OSINT platform for recon, OSINT investigations, link analysis, and more. Offline. Local-first workflows. No cloud dependency required.
OMOIKA'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 omoika[all]For development:
git clone https://github.com/omoika-institute/framework.git
cd framework/
pip install -e ".[dev]"from omoika import Plugin
from omoika.elements import TextInput, Copy
from omoika.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),
Copy(label="Domain"),
]from omoika 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"),
)omoika run -T '{"label": "email", "version": "1.0.0", "transform": "extract_domain", "data": {"email": "[email protected]"}}'| 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 omoika import load_plugins_fs
load_plugins_fs("/path/to/my-plugins", "my_plugins")# List entities and transforms
omoika ls entities
omoika ls transforms -L email
# Run a transform
omoika transform '{"label": "email", "version": "1.0.0", "transform": "to_domain", "data": {...}}'
# Get entity blueprints
omoika blueprints -L email
# Initialize a new plugins project
omoika init
# Sync manifest and README metadata after repo changes
omoika sync
# Compile JSON entity to Python
omoika compile entity.json -O entity.py- Python 3.13+
MIT License, see LICENSE for details.