App Extension
App Extension
Other Logpresso apps can register their own node/relationship types, automatic loading
builders, and threat intel tabs into the graph. Place a sonar_graph.json manifest at
the app bundle root (src/main/resources in a Maven project) and it is registered
automatically when the app starts and unregistered when it stops. No API calls are
needed.
see docs/app-registration.md in the sonar-graph repository.
Manifest Structure
{
"manifest_version": "1.0.0",
"node_defs": { "<type>": { ... } },
"edge_defs": { "<relationship>": { ... } },
"builders": [ { ... } ],
"ip_intels": { "<key>": { ... } }
}
All four sections are optional. An app that only provides threat intel tabs needs just
ip_intels.
node_defs — Node Types
Besides names/descriptions (en·ko·ja) and attrs (attribute definitions with types,
enums, and ranges), a node type can declare its value format:
value_examples— per-locale value examples, shown as the input placeholder in the Knowledge Graph's Add Node screen.value_pattern— a regular expression the value must match, validated both in the UI and by the batch commands.
"myapp-scanner": {
"type": "myapp-scanner",
"names": { "en": "Scanner", "ko": "스캐너" },
"descriptions": { "en": "Scanner appliance keyed by serial", "ko": "시리얼로 식별되는 스캐너 장비" },
"value_examples": { "en": "SN-2024-0001" },
"value_pattern": "^SN-\\d{4}-\\d{4}$",
"attrs": {}
}
edge_defs — Relationship Types
Declare the allowed source/target type combinations with pairs. They drive the
"Connectable node types" guidance in the Knowledge Graph connection screen and are also
enforced on writes. A relationship without pairs is generic and accepts any combination.
"scanned-by": {
"type": "scanned-by",
"names": { "en": "Scanned By", "ko": "스캔 수행" },
"descriptions": { "en": "ipv4-addr scanned by myapp-scanner", "ko": "IP가 스캐너에 의해 점검됨" },
"attrs": {},
"pairs": [ { "src": "ipv4-addr", "dst": "myapp-scanner" } ]
}
builders — Automatic Loading
A declarative batch that runs a query on a cron schedule to populate the graph. The schedule is registered when the app starts and disabled when it stops.
"builders": [
{
"cron_schedule": "0 5/30 * * * ?",
"names": { "en": "Sync scanners", "ko": "스캐너 동기화" },
"descriptions": { "en": "Sync scanner inventory", "ko": "스캐너 인벤토리 동기화" },
"query": "myapp-scanners | eval type=\"myapp-scanner\", value=serial | sonar-set-node-batch run=t"
}
]
ip_intels — Threat Intel Tabs
Adds tabs to IP cards in the Threat Intel screen. The target IP is injected into the
$("ip") placeholder of query. A single type renders as a property sheet, a
multiple type as a table. The app's icon appears next to the tab name automatically.
"ip_intels": {
"myapp-ports": {
"type": "multiple",
"names": { "en": "Open Ports", "ko": "열린 포트" },
"descriptions": { "en": "Ports detected for the IP", "ko": "탐지된 열린 포트" },
"query": "myapp-open-ports ip=$(\"ip\")",
"fields": [
{ "key": "port", "names": { "en": "Port", "ko": "포트" } },
{ "key": "service", "names": { "en": "Service", "ko": "서비스" } }
]
}
}
Cautions
- Type names have no namespace. Registering the same type name lets the later-loaded app
overwrite it, so prefix app-specific types (e.g.
myapp-). - Malformed JSON is ignored silently. If registration seems to do nothing, use the checks below.
- Builders run only when the app is installed in Sonar.
Verifying Registration
Check that your types appear in the results. Verify builders with sonar-graph-builders,
and threat intel tabs by opening an IP card in the Threat Intel screen.