Janus Ingestor Creation
Overview
Use this skill when Janus needs to ingest a new telemetry source or change how an existing source is pulled, loaded, normalized, or wired into the CLI.
Start from Janus's existing ingest pipeline. Do not create a parallel pipeline unless the user explicitly asks for one.
Workflow
- State the source and mode in one sentence:
- live API pull
- local file/directory load
- hybrid pull plus local normalization
- Read the core ingest path first:
janus.py
Core/models.py
Core/io.py
Core/output_rule.py
Config/janus.example.yml
docs/architecture.md
- If
janus-cli should expose the ingestor, also read:
cmd/janus-cli/config.go
cmd/janus-cli/main.go
cmd/janus-cli/status.go
cmd/janus-cli/docker.go
- Read the closest existing source implementation:
- Mythic live API:
Parsers/Mythic/mythic_pull.py
- Partial/local Mythic:
Parsers/Mythic/partial_data_adapter.py
- Ghostwriter live/export:
Parsers/Ghostwriter/ghostwriter_pull.py, Parsers/Ghostwriter/main.py
- Cobalt Strike REST live API:
Parsers/CobaltStrike/cobalt_strike_rest.py
- Outflank local logs:
Parsers/Outflank/outflank_log.py
- Inspect the provided sample data, client, schema, or API docs before asking the user. Ask only for missing product intent or an undiscoverable endpoint/schema detail.
- Implement the smallest source-specific parser that preserves the canonical Janus event model.
For the detailed implementation checklist and source-mode guidance, read references/ingestor-patterns.md.
Event Model Rules
- Normalize all sources into
TaskEvent and ResultEvent.
- Join task/result events by
(operation_id, task_id).
- Use
normalize_timestamp() for all timestamps.
- Keep parser-specific quirks in the parser module, not in analyzers.
- For string source IDs, derive stable positive integer IDs, and preserve the raw source task ID in
c2_task_id when available.
- Emit conservative
status values:
- Use
success / error only when the source provides reliable status or a narrowly documented inference.
- Use
unknown when status fidelity is weak.
- Apply
arguments_rule and output_rule before writing events.ndjson.
- Record parser quality counters in
bundle.json: row counts, skipped counts, invalid timestamps, fallback IDs, status counts, and source provenance.
Integration Rules
- Put new parser code under
Parsers/<Source>/; do not overwrite or repurpose existing ingestors.
- Keep source identifiers stable and lowercase, such as
mythic, ghostwriter, cobaltstrike-rest, or outflank.
- Add explicit Python CLI support for the new source or loader in
janus.py.
- Add Go wrapper support when the source should work through
janus-cli pull or janus-cli run.
- Update config examples, status/config output, README usage, and architecture/FAQ docs when behavior is user-facing.
- For live APIs, add auth/preflight behavior and TLS defaults consistent with existing sources.
- For local files, document Docker mount assumptions;
janus-cli normally exposes ./out and ./Config to the container.
Validation
Always run at least one real CLI/container path after implementation:
- Parser-level import or focused unit test.
- Source load/pull into
events.ndjson and bundle.json.
analyze --all against the generated events.
html report generation.
go test for cmd/janus-cli when Go wrapper code changed.
Report source fidelity limits in the final answer, especially for status inference and partial/local datasets.
1---2name: janus-ingestor-creation3description: Use this skill for Janus requests to design, add, or adjust source ingestors/parsers for live APIs or local files, including Mythic, Ghostwriter, Cobalt Strike REST, Outflank implant logs, new C2 telemetry sources, parser normalization, pull/load CLI wiring, source metadata, retention policy handling, and Docker janus-cli integration.4---56# Janus Ingestor Creation78## Overview910Use this skill when Janus needs to ingest a new telemetry source or change how an existing source is pulled, loaded, normalized, or wired into the CLI.1112Start from Janus's existing ingest pipeline. Do not create a parallel pipeline unless the user explicitly asks for one.1314## Workflow15161. State the source and mode in one sentence:17 - live API pull18 - local file/directory load19 - hybrid pull plus local normalization202. Read the core ingest path first:21 - `janus.py`22 - `Core/models.py`23 - `Core/io.py`24 - `Core/output_rule.py`25 - `Config/janus.example.yml`26 - `docs/architecture.md`273. If `janus-cli` should expose the ingestor, also read:28 - `cmd/janus-cli/config.go`29 - `cmd/janus-cli/main.go`30 - `cmd/janus-cli/status.go`31 - `cmd/janus-cli/docker.go`324. Read the closest existing source implementation:33 - Mythic live API: `Parsers/Mythic/mythic_pull.py`34 - Partial/local Mythic: `Parsers/Mythic/partial_data_adapter.py`35 - Ghostwriter live/export: `Parsers/Ghostwriter/ghostwriter_pull.py`, `Parsers/Ghostwriter/main.py`36 - Cobalt Strike REST live API: `Parsers/CobaltStrike/cobalt_strike_rest.py`37 - Outflank local logs: `Parsers/Outflank/outflank_log.py`385. Inspect the provided sample data, client, schema, or API docs before asking the user. Ask only for missing product intent or an undiscoverable endpoint/schema detail.396. Implement the smallest source-specific parser that preserves the canonical Janus event model.4041For the detailed implementation checklist and source-mode guidance, read `references/ingestor-patterns.md`.4243## Event Model Rules4445- Normalize all sources into `TaskEvent` and `ResultEvent`.46- Join task/result events by `(operation_id, task_id)`.47- Use `normalize_timestamp()` for all timestamps.48- Keep parser-specific quirks in the parser module, not in analyzers.49- For string source IDs, derive stable positive integer IDs, and preserve the raw source task ID in `c2_task_id` when available.50- Emit conservative `status` values:51 - Use `success` / `error` only when the source provides reliable status or a narrowly documented inference.52 - Use `unknown` when status fidelity is weak.53- Apply `arguments_rule` and `output_rule` before writing `events.ndjson`.54- Record parser quality counters in `bundle.json`: row counts, skipped counts, invalid timestamps, fallback IDs, status counts, and source provenance.5556## Integration Rules5758- Put new parser code under `Parsers/<Source>/`; do not overwrite or repurpose existing ingestors.59- Keep source identifiers stable and lowercase, such as `mythic`, `ghostwriter`, `cobaltstrike-rest`, or `outflank`.60- Add explicit Python CLI support for the new source or loader in `janus.py`.61- Add Go wrapper support when the source should work through `janus-cli pull` or `janus-cli run`.62- Update config examples, status/config output, README usage, and architecture/FAQ docs when behavior is user-facing.63- For live APIs, add auth/preflight behavior and TLS defaults consistent with existing sources.64- For local files, document Docker mount assumptions; `janus-cli` normally exposes `./out` and `./Config` to the container.6566## Validation6768Always run at least one real CLI/container path after implementation:6970- Parser-level import or focused unit test.71- Source load/pull into `events.ndjson` and `bundle.json`.72- `analyze --all` against the generated events.73- `html` report generation.74- `go test` for `cmd/janus-cli` when Go wrapper code changed.7576Report source fidelity limits in the final answer, especially for status inference and partial/local datasets.77