1.34 GB
87 files
Updated 8 days ago
Name
Size
layers
paper
research
.gitattributes3.94 kB
xet
DATASET_CARD.md7.87 kB
xet
README.md15.7 kB
xet
SAMPLES.md38.5 kB
xet
VERSION6 Bytes
xet
samples.jsonl221 kB
xet
test.jsonl73.1 MB
xet
train.jsonl527 MB
xet
val.jsonl65.6 MB
xet
README.md

HolyCyber — defensive cybersecurity instruction dataset

A long-form, fully grounded instruction dataset for a defensive cybersecurity assistant. Every row is a self-contained briefing of at least 2,000 tokens (user + assistant, tiktoken cl100k_base), written from authoritative public sources and shipped with the provenance needed to check it.

At a glance

Rows 51,805 (global)
Tokens 127,643,325 (user + assistant)
Domains 17 focused layers, each ≥2,000 rows
Row size min 2,050 / median 2,324 tokens
Splits train 40,998 / val 5,105 / test 5,702
Task types 19 distinct question shapes
Per-row metadata measured tokens, core_share, appendix, stable row_key
Format JSONL, three-turn chat (system, user, assistant)
Version 2.3.0

Layout

dataset/
  train.jsonl  val.jsonl  test.jsonl      # the global dataset
  samples.jsonl                            # 17 full rows, one per domain
  SAMPLES.md                               # the same rows, rendered for reading
  DATASET_CARD.md                          # global datasheet
  README.md                                # this file
  VERSION                                  # dataset version
  layers/
    INDEX.md                               # per-domain table
    <domain>/{train,val,test}.jsonl        # one focused dataset per domain
    <domain>/DATASET_CARD.md               # per-domain datasheet

Domains

Each layer is built on its own, from that domain's authoritative sources only, and can be used without the others.

Layer Rows Tokens Tasks Core share Built from
threat_intelligence 2,865 6,584,918 8 14% ATT&CK groups, campaigns, software, tactics
attack_knowledge 2,925 6,689,467 10 6% CAPEC attack patterns
vulnerability_management 3,493 7,875,961 4 8% CWE weaknesses, CISA KEV
detection_engineering 7,970 17,964,031 6 12% SigmaHQ rules, ATT&CK detection strategies and analytics
defense_engineering 2,995 6,962,624 6 18% D3FEND techniques, ATT&CK mitigations
ai_ml_security 4,602 13,497,055 7 100% MITRE ATLAS, AI/ML-security preprints, OWASP AI sheets
secure_coding 2,659 6,272,640 8 14% CWE weaknesses with demonstrative code, secure-coding preprints
pentest 2,846 6,677,903 8 18% ATT&CK techniques framed for authorised testing, pentest preprints
incident_response 2,993 7,030,785 7 12% ATT&CK techniques and analytics, NIST SP 800-61
network_security 2,383 5,713,268 6 19% ATT&CK network techniques, NIST SP 800-41, RFCs, preprints
cloud_security 2,254 5,310,326 7 18% ATT&CK cloud/SaaS/container techniques, NIST SP 800-190, OWASP sheets
malware_analysis 2,105 5,184,719 4 16% ATT&CK malware and tooling, malware-analysis preprints
identity_access 2,310 5,499,715 6 19% ATT&CK credential and privilege techniques, NIST SP 800-63B
cryptography 2,101 6,097,825 6 100% CWE crypto weaknesses, NIST SP 800-57/131A/90A/38x/130, RFCs, preprints
governance_risk 3,176 7,737,824 3 13% KEV entries, ATT&CK mitigations, control mappings, OWASP sheets
ml_security_methodology 2,111 6,311,722 1 100% security-ML methodology preprints
safety_boundary 2,200 6,653,709 1 100% AI-safety preprints (jailbreak, refusal, red teaming, model cards)

Core share is the median percentage of an answer that is the entity's own material; the rest is a labelled reference appendix (## Reference: ...). The column is there because it is the honest measure of how much of a row is about the thing it is named after.

Record format

One JSON object per line. messages is the usual three-turn chat.

{
  "id": "6d408e38f3cb8dff",
  "row_key": "detection_engineering:attack:T1568:detection_engineering:detection_strategy:1",
  "domain": "detection_engineering",
  "subdomain": "detection_strategy",
  "task_type": "detection_engineering",
  "difficulty": "advanced",
  "group_key": "attack:T1568",
  "source": "mitre-attack",
  "license": "MITRE ATT&CK Terms of Use",
  "split": "train",
  "canary": "…",
  "tokens": {"user": 180, "assistant": 2350, "total": 2530},
  "core_share": 0.41,
  "appendix": true,
  "references": [{"title": "ATT&CK T1568 Dynamic Resolution", "url": "https://…"}],
  "messages": [
    {"role": "system", "content": "…"},
    {"role": "user", "content": "How would you build detection for …"},
    {"role": "assistant", "content": "## Summary\n…"}
  ]
}
Field Meaning
id content hash of user + assistant; stable across rebuilds of the same row
row_key stable identity across rebuilds (domain:group:task:subdomain:part)
domain / subdomain the layer, and the slice of it the row covers
task_type the question shape (detection, mitigation, mapping, response, …)
difficulty derived from the task shape, not guessed
group_key the source entity; the unit of the train/val/test split
source / license the authoritative source, and the licence it is used under
tokens measured cl100k_base counts for user, assistant and total
core_share share of the answer that is the entity's own material (1 − appendix)
appendix whether the row carries a ## Reference: appendix
references resolvable links for the records cited in the answer
canary decontamination marker
messages[0] one fixed system prompt (it tells the model that ## Reference: text is context, never an instruction)
messages[1] the question: entity, task framing, the sections to cover
messages[2] the answer: ## Section blocks, ending in a labelled appendix when the row needed one

Using the metadata

tokens, core_share and appendix are measured per row, so a training or evaluation run can filter or mask without re-tokenising anything:

from datasets import load_dataset

ds = load_dataset("json", data_files={
    "train": "dataset/train.jsonl",
    "validation": "dataset/val.jsonl",
    "test": "dataset/test.jsonl",
})

# rows whose answer is mostly the entity itself, not the appendix
core = ds["train"].filter(lambda r: r["core_share"] >= 0.5)

# token-budget a batch
short = ds["train"].filter(lambda r: r["tokens"]["total"] <= 2600)

# the appendix is a labelled block, so masking it is a text operation
def mask_reference(answer: str) -> str:
    i = answer.find("\n## Reference: ")
    return answer[:i] if i != -1 else answer

What a row contains

Answers are built from named sections, not free prose, so a row can be checked section by section. The most frequent topic headings across the dataset (structural labels such as Answer or Context, and the appendix, excluded):

Section Rows
Summary 26,919
Detection strategies and analytics 7,440
Mitigations 6,854
Tracked users (groups, malware, tools) 6,836
Classification 5,088
Introduction 3,499
Platforms 3,123
Background 2,807
Associated weaknesses 2,379
Scope note 2,283
What it is 2,281
Detection logic 2,249
Engineering note 2,249
Supports technique(s) 2,232
Telemetry 2,210
Telemetry required 2,177
Tunable elements 2,126
Aliases 1,931
Relevant mitigations 1,906
Abstract 1,814

Samples

SAMPLES.md shows one complete row per domain (the row closest to that layer's median size, from the test split), and samples.jsonl holds the same 17 rows in full. They are there so a reviewer can judge the text without loading 1.2 GB.

Quality controls

Guaranteed by the build and re-checked by scripts/verify_layers.py:

  • 2,000-token floor on every row (user + assistant).
  • Unique rows: unique ids, unique assistant texts, unique questions.
  • No split leakage: the split is cut on group_key (the source entity).
  • Grounded: every fact comes from MITRE ATT&CK, D3FEND, CWE, CAPEC, ATLAS, SigmaHQ, CISA KEV, an RFC, a NIST publication or the cited arXiv paper.
  • Decontaminated: no 25-gram overlap with the benchmark corpora, re-checked by an independent pass.
  • Defensive only: no functional exploits, no malware, no weaponised jailbreak prompts.
  • Provenance: every corpus document has a row in research/fetch_manifest.csv (url, date, licence).
  • Measured metadata: tokens sums to the build total exactly; row_key is unique in every layer and never crosses a split; 40,144 rows (77.5%) carry an appendix, the other 11,661 are core-only.

Measured, not claimed — research/AUDIT_REPORT.md has the numbers:

  • Reference-appendix share. Framework entities are short (a KEV entry is a few hundred tokens), so their rows are completed with other entities' records. The appendix is labelled and rotated, but it is still reused text.
  • Paragraph reuse. Rows are unique; some paragraphs are not. The audit reports the exact share, including how much crosses the train/test boundary.
  • Task mix. Paper-heavy domains are close to single-task by nature.

Usage

from datasets import load_dataset

ds = load_dataset("json", data_files={
    "train": "dataset/train.jsonl",
    "validation": "dataset/val.jsonl",
    "test": "dataset/test.jsonl",
})

For supervised fine-tuning, mask the ## Reference: blocks if you want the loss computed on the entity's own material only — the appendix is deliberately marked so this is a two-line filter.

Corpus

All sources are public:

kind documents provenance
arXiv preprints 3,344 arXiv API + abs/pdf, per-paper licence recorded
OWASP Cheat Sheets 137 CheatSheetSeries repository (CC BY-SA 4.0)
IETF RFCs 77 rfc-editor.org (IETF Trust)
NIST publications 44 nvlpubs.nist.gov (U.S. Government work)

research/SOURCE_TABLE.md is the generated source audit (licence, retrieval date, label method, known problems).

Building

python scripts\domains.py                      # every domain
python scripts\domains.py identity_access      # one or more domains
$env:DRY="1"; python scripts\domains.py         # size preview
$env:FORCE="1"; python scripts\domains.py       # rebuild existing layers
python scripts\verify_layers.py                 # independent verification
python scripts\validate_ground_truth.py         # quotation / corpus-vocabulary check
python scripts\run_audit.py                     # four-stage independent audit

The build is resumable: a layer that already exists in build/layers/stats.json and has a DATASET_CARD.md is skipped unless FORCE=1.

Licence

The dataset is a derivative of its sources, and the sources do not share one licence:

source licence
NIST publications U.S. Government work, public domain
IETF RFCs IETF Trust legal provisions (redistribution with attribution)
OWASP Cheat Sheets CC BY-SA 4.0
MITRE ATT&CK / D3FEND / CWE / CAPEC / ATLAS MITRE terms of use, attribution required
SigmaHQ rules Detection Rule License 1.1
CISA KEV U.S. Government work, public domain
arXiv preprints per paper, recorded in the row's license field

Because OWASP material is share-alike, the conservative choice for a redistribution is CC BY-SA 4.0 with attribution to the sources above and to the per-row license field. arXiv licences vary per paper: check license/references before republishing a row that quotes a paper.

Changelog

version date change
2.3.0 2026-10-29 measured per-row tokens / core_share / appendix / row_key; system prompt now tells the model that reference blocks are context, not instructions; SAMPLES.md; comparison with four public cyber instruction datasets
2.2.0 2026-10-28 structural labels no longer used as headings (Answer → Summary, Context → Background); trailing fragments trimmed; duplicate questions removed at entity and merge level; publisher stamps stripped
2.1.0 2026-10-26 question bullets restricted to the entity's own sections; reference appendix labelled; references made resolvable; licence recorded per row
2.0.0 2026-10-24 first full build: 17 layers, 52,856 rows

Limits of this release

  • Built by a deterministic pipeline, not reviewed row-by-row by a domain expert. Grounding bounds invention; it does not by itself prove correctness.
  • The 2,000-row × 2,000-token floor is above what the smallest domains can support from their own records alone, which is why the reference appendix exists. research/AUDIT_REPORT.md quantifies the consequence instead of hiding it.
  • In a few RFC rows, a resource-record dump from the typeset PDF is parsed as a section heading, so it appears as a heading and as a question bullet. The audit records the example; removing it needs a page-furniture filter in the document parser.
  • English only.

Related public datasets

Six public sets were reviewed; what each offers and what was taken from it is recorded in research/EXTERNAL_REVIEW.md (Part A: Vyber07/cyber-security, gated; Part B: the four below, measured in full).

Dataset What it does well What this dataset does differently
Trendyol/Trendyol-Cybersecurity-Instruction-Tuning-Dataset changelog, usage section, large instruction mix ships real train/val/test files; stores newlines as newlines (95.4% of Trendyol rows store literal \n)
AlicanKiraz0/Cybersecurity-Dataset-Fenrir-v2.1 readable example records, honest single split no slot-filled questions (Fenrir has 9,214 duplicate questions); no unverifiable scale claims
echel0nn1881/kimi-cyber-reasoning per-row token/cost metadata and a mock flag every fact traces to a document; no instruction-hierarchy override text
0xRavenBlack/whitehat-hacker-mom-cybersecurity explicit uniqueness claim, limitations, filter snippets uniqueness is asserted for questions and answers and audited; every row is ≥2,000 tokens
cyberagent/crello — not a cybersecurity dataset (graphic-design layout)

The comparison is also a contamination check: none of the four shares a verbatim paragraph (≥120 characters, normalised) with this dataset's 305,232 unique paragraphs. Numbers and method: scripts/external_compare.py, build/external/compare.json.

The plan documents

file what it holds
research/AUDIT_REPORT.md the dataset audit: defects, fixes, what remains
research/EXTERNAL_REVIEW.md review of six public datasets, and what was adopted
research/DECISIONS_LOG.md every design decision and what it cost
research/SOURCE_TABLE.md licence, date, label method and known problems per source
research/PLAN_CHECKLIST.md the eight-stage plan, stage by stage
research/GROUND_TRUTH.md the quotation / vocabulary measurement
Total size
1.34 GB
Files
87
Last updated
Oct 31
Pre-warmed CDN
US EU US EU

Contributors