DS-0007

Synthetic network beacon flows

A teaching set for supervised learning: is this phone's flow pattern to one destination command-and-control style beaconing, or normal app traffic? 2,964 destination summaries, train and test files, reference answers and a guide. Every value is invented and no row comes from a device or a network.

Synthetic filesVersion 1.0Data CC BY 4.0Code MIT

At a glance

What the set is, how it was made, and how it may be used.

Plain CSV files, so any spreadsheet, text editor or SQLite browser opens them. Try the challenge: Find the beacon in phone traffic (Expert).

Question
Binary classification: beaconing (1) or normal app traffic (0) for one destination seen from one phone over 24 hours.
Device class
None (generated rows from 150 invented phones)
Evidence type
Synthetic flow-summary table
Made on
A script (generate.py, Python standard library) with seed 20261107. No capture, no device and no real address. The same seed gives the same bytes.
Contents
2,964 rows: 2,075 train (11.7% beaconing) from 105 phones and 889 test (11.7% beaconing) from 45 other phones. 18 traffic families, 5 beaconing and 13 normal.
Features
Flow count, interval mean, jitter and regularity, up/down ratio, payload spread, time-of-day spread, destination age bucket, SNI present, protocol, session length.
Size
142 KB as a zip (144,993 bytes), 16 files in the zip
Version
1.0
Licence
Data: CC BY 4.0. Code: MIT.
Maintainer
Daniel Addai, SectraTEK Lab

What is inside

One zip with the data, the reference answers, the code to try it, a data dictionary and a teaching guide.

File tree
network-beacon-flows/
  data/
    train.csv                   2075 rows, with label
    test.csv                    889 rows, no label
  answers/
    reference_answers.csv       flow_id, split, device_id, label, family
    reference_summary.json      counts, family notes, baseline scores
  code/
    baseline.py                 rule, logistic regression, tree, forest
    baseline_sklearn.py         optional, needs scikit-learn
    evaluate.py                 scores a predictions file
  generator/
    generate.py                 writes everything from seed 20261107
    templates/                  text for README, dictionary, guide
  DATA_DICTIONARY.md
  TEACHING_GUIDE.md
  README.txt
  LICENCE.txt
Columns in train.csv and test.csv
ColumnMeaning
flow_id, device_idRow id and the invented phone it belongs to. Identifiers, not features. Train and test hold different phones.
protocoltcp_tls, tcp_plain, udp_quic or udp_other.
flow_countFlows to this destination in 24 hours.
interval_mean_s, interval_jitter_pct, interval_regularityMean gap between flow starts, its spread as a percent of the mean, and the share of gaps within 20 percent of the median.
up_down_ratio, payload_size_cv, bytes_total_kbDirection of the bytes, how much flow sizes vary, total volume.
active_hours, night_hour_shareClock hours with a flow (1 to 24) and the share of those hours between 00:00 and 05:59.
dest_age_bucket, sni_presentAge of the destination (lt_7d, 7_30d, 30_365d, gt_365d, unknown) and whether a server name was sent.
session_len_sMedian flow duration.
triage_tagLeakage column: written after review, follows the label. Do not use as a feature.
labeltrain.csv only. 1 beaconing, 0 normal.

The column ideas are the ones network analysts already use: fixed or jittered sleep times between check-ins (MITRE ATT&CK T1029 Scheduled Transfer), traffic that hides in ordinary protocols (T1071 Application Layer Protocol), encrypted channels (T1573), and the per-connection fields of a Zeek conn.log. The data dictionary says how to compute each column from a capture or a flow log. Hidden endpoints are a real limit on phones: in LAB-TR-0012 the endpoint of 1,235,369 of 1,238,152 network connection lines in one decoded iOS 27.0 log was a hash tag, which is what the unknown destination age stands for.

Reference answers and documented traps

18 traffic families. Regular traffic that is benign (push, keep-alives, time sync, update and device-management check-ins) is regular on purpose, and low-and-slow beacons with jitter are hard on purpose. The reference answers give the label and the family of every row; they are kept outside the data folder so you can predict first and compare after.

Traffic families in DS-0007 and the reference answer for each
FamilyAnswerWhat it looks likeTrain rowsTest rows
burst_beaconbeaconingSleep, then a short burst of check-ins.2914
fixed_beaconbeaconingFixed sleep time, almost no jitter.6124
fronted_beaconbeaconingCheck-in to an old, widely used destination with an SNI.2814
jitter_beaconbeaconingSleep time with 10 to 50 percent jitter.6923
lowslow_beaconbeaconingHours between check-ins, with jitter; few flows a day.5629
browsingnormalHuman-driven; irregular, large and variable payloads.438186
ip_direct_appnormalLegitimate app that connects by address, no SNI.6727
keepalivenormalApp heartbeat; tiny, regular payloads.16557
mdm_checkinnormalDevice-management check-in every few hours.5216
new_cdn_appnormalLegitimate app on a recently registered domain.8242
pushnormalPush-notification channel; regular on purpose.11350
social_refreshnormalFeed refresh while an app is in use; large jitter.21190
streamingnormalA few long, download-heavy sessions.12851
syncnormalMail, calendar or cloud sync; periodic with jitter.195100
telemetrynormalAnalytics upload; periodic, upload-heavy.20883
timesyncnormalNetwork time exchange; the most regular, smallest flows.5026
update_checknormalUpdate check; a handful of flows a day.7839
vpn_tunnelnormalAlways-on VPN or proxy keep-alive; small, regular, no SNI.4518

The traps

  • Leakage. triage_tag is written after an analyst looked at the flow. On the test file, reading "escalated or watchlist" as beaconing gives F1 0.909, higher than every baseline below. A model that uses it has learned the answer from the answer.
  • Imbalance. About 11.7 percent of the test rows are beaconing. Answering "normal" every time scores a high accuracy and finds nothing; run it and read it.
  • The fixed-interval shortcut. The rule "jitter under 5 percent" is one of the shipped baselines (baseline.py --model rule). Run it and score it to see how many test flows it flags and how many of those are normal. It catches most fixed-sleep beacons and misses most jittered, low-and-slow, burst and fronted ones. The normal flows it flags are mostly push, time-sync and update-check traffic.
  • Look-alike rows. Beaconing rows from one phone share a configuration. Random folds on the training file give a forest an average precision of 0.803; folds that keep each phone together give 0.760.
  • Blank values. Destinations seen once a day have no interval statistics: 25 train rows have no mean gap and 90 have no jitter figure.
  • Overlap. About half of the beaconing destinations copy the size, direction, session and destination profile of a normal app.

Baseline scores on the test file

Baseline scores on the DS-0007 test file
ModelPrecisionRecallF1False-alarm rateROC AUCAverage precision
Logistic regression0.2470.7020.3660.2830.8110.388
Decision tree, depth 50.4490.7980.5740.1300.8700.613
Forest of 25 trees0.5560.7600.6420.0800.9450.767

The fixed-interval rule is not in this table, because question 4 of the challenge asks for its false positives: run it and read it off. Threshold 0.5 on a class-balanced fit. Scores are from the shipped baseline.py and evaluate.py. These numbers are for this synthetic set only. They are a floor for the exercise, not a target, and say nothing about real traffic.

One worked example: two flows that look the same in time

Two training rows chosen because their timing columns almost match: mean gap, jitter and regularity. Row A (f2124, family push) is normal. Row B (f0013, family fixed_beacon) is beaconing. A rule that reads only the interval columns cannot tell them apart.

Two training rows with almost the same timing and different labels
ColumnRow A, normalRow B, beaconing
Flows in 24 h141139
Mean gap (s)622.7618.8
Jitter (%)8.48.5
Regularity0.9930.993
Up / down0.7650.906
Payload spread (CV)2.0120.810
Total (KiB)82.691.9
Session (s)9.472.55
Destination agegt_365dgt_365d
SNI present10
Protocoltcp_tlstcp_plain
triage_tag (leakage)noneescalated

The timing columns match; these differ by more than a quarter: payload spread, session length, SNI and protocol. In a real case a difference like that is where to look next, and no single column decides it, which is why the baselines use all of them. The triage_tag row shows the leakage column giving the answer away; a real decision does not have that column yet. Values are invented.

Try it with free tools

  1. Check the download. Compute the SHA-256 of the zip and compare it with the value below: python -c "import hashlib,sys;print(hashlib.sha256(open(sys.argv[1],'rb').read()).hexdigest())" network-beacon-flows-1.0.zip. Then unzip it.
  2. Look at the rows. Open data/train.csv in a plain text editor or a spreadsheet and sort by interval_jitter_pct. Read DATA_DICTIONARY.md beside it.
  3. Ask a question in SQLite. In DB Browser for SQLite choose File, Import, Table from CSV file, pick train.csv and run the query below.
  4. Run a baseline. With Python 3.8 or later and nothing to install: cd code, then python baseline.py --model forest --out predictions.csv.
  5. Score it. python evaluate.py predictions.csv. Then try --model rule, logreg and tree, and read TEACHING_GUIDE.md for the session plan.
SQL (SQLite)
SELECT dest_age_bucket,
       COUNT(*) AS flows,
       SUM(CAST(label AS INTEGER)) AS beacons,
       ROUND(100.0 * SUM(CAST(label AS INTEGER))
             / COUNT(*), 1) AS pct_beacon
FROM train
GROUP BY dest_age_bucket
ORDER BY pct_beacon DESC;
Result on train.csv
dest_age_bucket | flows | beacons | pct_beacon
lt_7d | 75 | 13 | 17.3
unknown | 128 | 21 | 16.4
30_365d | 283 | 41 | 14.5
7_30d | 119 | 16 | 13.4
gt_365d | 1470 | 152 | 10.3

Destination age alone is a weak clue here: young destinations carry a legitimate app family too.

python evaluate.py predictions.csv (forest, test file)
test flows 889, beaconing 104, normal 785
threshold 0.500 (applies to the score column; a label column is used as given)
confusion: TP 79  FP 63  FN 25  TN 722
precision 0.556   recall 0.760   F1 0.642   false-alarm rate 0.080
accuracy 0.901 (always answering normal gives 0.883)   balanced accuracy 0.840
ROC AUC 0.945   average precision 0.767   precision@104 0.654   recall at 5% false alarms 0.663

beaconing families (share flagged):
  burst_beacon      14 flows  flagged  10  (0.71)
  fixed_beacon      24 flows  flagged  19  (0.79)
  fronted_beacon    14 flows  flagged  13  (0.93)
  jitter_beacon     23 flows  flagged  15  (0.65)
  lowslow_beacon    29 flows  flagged  22  (0.76)
normal families (false-alarm share):
  browsing         186 flows  flagged   0  (0.00)
  ip_direct_app     27 flows  flagged   5  (0.19)
  keepalive         57 flows  flagged  21  (0.37)
  mdm_checkin       16 flows  flagged   6  (0.38)
  new_cdn_app       42 flows  flagged   0  (0.00)
  push              50 flows  flagged   3  (0.06)
  social_refresh    90 flows  flagged   0  (0.00)
  streaming         51 flows  flagged   0  (0.00)
  sync             100 flows  flagged  12  (0.12)
  telemetry         83 flows  flagged  13  (0.16)
  timesync          26 flows  flagged   0  (0.00)
  update_check      39 flows  flagged   0  (0.00)
  vpn_tunnel        18 flows  flagged   3  (0.17)

Get the dataset

One zip, 144,993 bytes. It holds 16 files: the data, the reference answers, the code, the data dictionary, the teaching guide, the licences and the generator. Check it against the hash below.

Download network-beacon-flows-1.0.zip (142 KB)

Served from this site, versioned. The manifest and the hashes are linked below.

SHA-256 of the zip

7b365db45d4b37216e5e3a474faa914e083d58a12dfe780eb60757ca35fa92d4

The loose files, manifest.json and SHA256SUMS.txt, sit beside the zip on this site. To rebuild every byte yourself, run python generate.py from the generator folder (Python 3, standard library only).

Licence at a glance

A plain-language summary. The full text is at creativecommons.org/licenses/by/4.0 and in LICENCE.txt inside the zip. This dataset needs no acceptance step: use the direct link above.

Data: Creative Commons Attribution 4.0 (CC BY 4.0)

  • You may copy and share the files in any medium or format, adapt them (for example to make test fixtures) and use them for any purpose, including teaching, research, casework and commercial work.
  • You must give credit using the citation on this page, link to the licence and say if you changed the files.
  • Please keep the hashes beside any copy you share, and say that the files are synthetic. Do not present them as taken from a real phone or a real network.

Code: MIT

generate.py, baseline.py, baseline_sklearn.py and evaluate.py are under the MIT licence. Keep the copyright notice with copies.

The dataset is provided as is, without warranty.

Hashes

Check every file after download. The set hash is the SHA-256 of manifest.json, which lists the size and hash of every other file.

Set hash (SHA-256, manifest.json)

b41cd1b7f648b4dd4f49177e76ae6a9a275b650e53abf5f085ad432056477285
Files in DS-0007 with SHA-256 hashes
FileContentSizeSHA-256
network-beacon-flows-1.0.zipArchive144,993 bytes
7b365db45d4b37216e5e3a474faa914e083d58a12dfe780eb60757ca35fa92d4
answers/reference_answers.csvReference answers: label and family per row86,180 bytes
714875dec07b5ca5845a32d9aaa0c86a089722d7095603af0578fcd461ba18aa
answers/reference_summary.jsonCounts and numbers quoted in the guide9,557 bytes
540dcf72e4bc2ac839074fb492bdaade81afd223c37e199c46240643945c2fbf
code/baseline.pyStandard-library baselines10,132 bytes
2740ad93d895190fafbff91e107528ccd94240816647bf8dfd41f6fe13be6ddb
code/baseline_sklearn.pyOptional scikit-learn baseline3,525 bytes
4676de4ff6e9f8d782d975772fc855642a88d6636be667c196384b2143ce906a
code/evaluate.pyScoring script7,754 bytes
58dd299d2fcc7afc153d32d9fbec8288e5666d991e31189a5be85071cea57f42
data/test.csvTest rows, no label79,113 bytes
0c1e7e09ff1749501e77318b5d45d375ed36a99e4c803733cbd42a2c42d03b71
data/train.csvTraining rows with label188,666 bytes
b5d6f1c0ceea46dba677cfbcca5acd8d3510ec07e6f655b35a5932f53ab67345
DATA_DICTIONARY.mdData dictionary5,948 bytes
8c15b679101aef7dabab1657c2c3477f2cec64069eb2106c493ff685105235c8
generator/generate.pyThe generator32,579 bytes
e559f9128e03026da012bea7986dc63e61c0b90305dfaaeef59f88eca1286238
generator/templates/DATA_DICTIONARY.md.tplGenerator template5,321 bytes
590adcfd4a3a376646b9d04c72ae3ef5cce560209dfde6faebc7636adfd82cd5
generator/templates/LICENCE.txt.tplGenerator template1,462 bytes
8e91e27d4fb6f725405c8a002f7f6a2554062540ed5adad0e053fb3335db3e96
generator/templates/README.txt.tplGenerator template2,170 bytes
bffd04e65e94eced694f75f7125b117c9a95605caea26b8f85c08049717883e1
generator/templates/TEACHING_GUIDE.md.tplGenerator template9,094 bytes
5137ce18fd2e28ced9da8800e49fc5c80e9f525c1e793a5ac65551fe7f949836
LICENCE.txtLicences1,446 bytes
756e527368c48f931c6f481e6e10e8c317d659bc50fbcd8ad7529d394240dac4
README.txtShort README2,114 bytes
ddd7b668d1b1f158bf39966e6b746c3d0e3008d50fe780933bc24d3859c0d51f
TEACHING_GUIDE.mdTeaching guide9,260 bytes
ff0278f7e1f9eba30ffd3a317debdc883c8d8d2b5abd67261c733c6050b0cf39

The zip holds the same files as the folder (the zip itself, manifest.json and SHA256SUMS.txt excepted). Together the files are 454,321 bytes before compression. Hashes and sizes on this page are read from manifest.json.

Cite this dataset

Cite as

Addai, D., SectraTEK Lab (2026). Synthetic network beacon flows (DS-0007), version 1.0. SectraTEK Lab Datasets. https://lab.sectratek.com/datasets/network-beacon-flows/. SHA-256 of manifest.json b41cd1b7f648b4dd4f49177e76ae6a9a275b650e53abf5f085ad432056477285

Citation guide
BibTeX
@dataset{sectratek-lab-ds-0007,
  author    = {Addai, Daniel},
  title     = {Synthetic network beacon flows},
  year      = {2026},
  version   = {1.0},
  publisher = {SectraTEK Lab},
  url       = {https://lab.sectratek.com/datasets/network-beacon-flows/},
  note      = {DS-0007, SHA-256 of manifest.json b41cd1b7f648b4dd4f49177e76ae6a9a275b650e53abf5f085ad432056477285}
}

Related

Datasets carry their own licence. This one is CC BY 4.0 for data and MIT for code. Articles are CC BY 4.0. All licences.