Contributing to OGI Transforms
March 8, 2026 ยท View on GitHub
Thank you for contributing to the OGI transform ecosystem! This guide walks you through creating and submitting a new transform.
Prerequisites
- Python 3.11+
- uv package manager
- A working OGI installation for testing
Creating a New Transform
1. Choose a Category
Pick the most appropriate category for your transform:
dns, email, web, ip, cert, social, hash, infrastructure, forensics
If none fit, propose a new category in your PR description.
2. Create the Directory Structure
transforms/<category>/<your-slug>/
plugin.yaml # Required: manifest
README.md # Required: documentation
CHANGELOG.md # Recommended: version history
transforms/
__init__.py # Required: empty or with imports
<your_transform>.py # Required: transform implementation
tests/ # Recommended
test_<name>.py
3. Write plugin.yaml
name: your-transform-slug # lowercase, hyphens only
version: "1.0.0" # semver required
display_name: "Your Transform"
description: "What this transform does"
author: "Your Name"
license: "AGPL-3.0"
category: "dns"
input_types: ["Domain"] # OGI entity types
output_types: ["IPAddress"] # OGI entity types
min_ogi_version: "0.3.0"
# Optional
tags: ["dns", "resolution"]
author_github: "yourusername"
python_dependencies:
- "some-package>=1.0.0"
api_keys_required:
- service: "example"
description: "API key from https://example.com"
env_var: "EXAMPLE_API_KEY"
transform_settings:
- name: "model"
display_name: "Model"
default: "gpt-4.1-mini"
field_type: "select"
options: ["gpt-4.1-mini", "gpt-4.1"]
- name: "max_results"
display_name: "Max Results"
default: "100"
field_type: "integer"
min_value: 1
max_value: 500
permissions:
network: true
filesystem: false
subprocess: false
4. Implement the Transform
Use api_keys_required in plugin.yaml to declare external services that need secrets.
Do not add *_api_key entries to transform_settings.
OGI manages secrets through the dedicated API Keys store and injects them into transforms at runtime.
Use transform_settings only for non-secret configurable options such as model names, limits, toggles, or modes.
If your transform needs a service key at runtime, read it from config.settings using the same <service>_api_key name implied by the declared service.
The frontend will show a Requires API key: <service> indicator automatically based on api_keys_required.
Your transform must extend BaseTransform from OGI:
from ogi.models import Entity, EntityType, EntityCreate, EdgeCreate, TransformResult
from ogi.transforms.base import BaseTransform, TransformConfig
class YourTransform(BaseTransform):
name = "your_transform"
display_name = "Your Transform"
description = "What it does"
input_types = [EntityType.DOMAIN]
output_types = [EntityType.IP_ADDRESS]
category = "DNS"
async def run(self, entity: Entity, config: TransformConfig) -> TransformResult:
entities = []
edges = []
messages = []
# Your logic here...
return TransformResult(
entities=entities,
edges=edges,
messages=messages,
)
5. Write a README
Your README should include:
- What the transform does
- Input and output entity types
- Any API keys required and how to get them
- A note that API keys are configured in OGI under
API Keys, not in transform settings - Example usage
- Known limitations
Submitting Your Transform
- Fork this repository
- Create a branch:
git checkout -b add-your-transform - Add your transform in
transforms/<category>/<slug>/ - Open a Pull Request using the provided template
- CI checks will run automatically
- Maintainers will review your code
CI Validation
Every PR is validated:
| Check | Tool | What it does |
|---|---|---|
| Schema validation | jsonschema | Validates plugin.yaml against the schema |
| Python linting | ruff | Code style and common errors |
| Security scan | bandit | Detects dangerous function calls |
| Pattern scan | semgrep | Blocks subprocess, ctypes, raw socket, __import__ |
| Structure check | Custom | Verifies required files exist |
| Tests | pytest | Runs tests if tests/ directory exists (30s timeout) |
Security Guidelines
- Never use
eval(),exec(),os.system(), orsubprocess - Never import
ctypesor use raw sockets - Always declare required permissions in
plugin.yaml - Always declare required API keys in
api_keys_required - Never store API keys in
transform_settings; OGI manages them in the dedicated API key store - Prefer
transform_settingsfor typed non-secret options (model selectors, limits, toggles) so OGI can validate and render a settings UI - Prefer
httpxfor HTTP requests (it's already an OGI dependency)
Entity Types
Available entity types in OGI:
Person, Domain, IPAddress, EmailAddress, PhoneNumber, Organization, URL, SocialMedia, Hash, Document, Location, ASNumber, Network, MXRecord, NSRecord, Nameserver, SSLCertificate, Subdomain, HTTPHeader
License
All contributions must be licensed under AGPLv3 to be compatible with OGI.