// THE CULT OF PRIVACY TECHNOLOGIESTwo topics. Everything you need.

ISNE 269497 · Semester 1, 2026 · Chiang Mai University. What can go wrong with personal data, and how to put a number on it. Written in plain words, with the real breaches the exam keeps coming back to.

Topic 1 · Threats, Risks & Vulnerabilities Topic 2 · Privacy Risk Quantification 36 cards 20-question mock exam 11 case files worked calculations works offline
Topic 1

Threats, Risks & Vulnerabilities

What can go wrong. You learn the 5 properties that keep data safe, the attacks that break them, the weak points in a network, and how people get re-identified from "anonymous" data.

Topic 2

Privacy Risk Quantification

How much can go wrong — as a number. You learn what makes data risky, how to measure risk with entropy, and how to check if de-identification actually worked (IG & RIG).

The map — 4 steps

1 · ProtectData has 5 properties to defend: confidentiality, integrity, availability, anonymity, indistinguishability.
2 · AttackAttackers break these properties: ransomware, DoS, SQL injection, phishing, re-identification.
3 · MeasureEntropy turns "how risky is this data?" into a number in bits.
4 · ReduceDe-identify the data, then use IG & RIG to prove the risk went down.

How to use this page

  • Notes — read each topic. Short and plain.
  • Case Files — real breaches. Exams love these.
  • Drills — 36 flashcards, then a 20-question mock exam with every answer explained.
  • Cheat Sheet — one last look before the exam.

Course facts

  • Instructor: Asst. Prof. Jenjira Jaimunk, PhD
  • Class: Tue & Fri 13:00–14:30, Room 601 Engineering Bldg.
  • Office hours: Monday 09:00–12:00
  • Grades: Homework & quizzes 30% · Midterm 35% · Final 35%
Exam tip: the discussion questions from the slides are inside the Quiz tab — practice them first, they are the most likely exam questions.

// LEARNNotes

Pick a topic, open one box at a time. Every box is one idea, and every idea earns its place on the exam.

1 Security vs Privacy — the two big words
Data SecurityInformation Privacy
Protects data from being stolen, changed, or destroyed.
Tools: encryption, firewalls, access controls, authentication, regular updates.
Controls who can see personal info and how it is used.
Covers: names, addresses, money and health info.

Privacy also means: collect only what is needed, be open about it, and get consent. People should know how and why their data is collected.

Memory trick: Security = protecting the data. Privacy = protecting the person behind the data.
2 The CIA triad — 3 security properties
PropertySimple meaningBroken by
ConfidentialityOnly the right people can see the data.Data breaches, ransomware (WannaCry), insiders selling secrets.
IntegrityThe data has not been changed — you can trust it.Hackers modifying data, phishing emails, fake news, misinformation.
AvailabilityYou can reach the data when you need it.DoS attacks — flooding a server until it fails (GitHub 2018).

DoS in one line: the attacker sends more requests than the service can handle, so real users get locked out.

3 The 2 privacy properties
PropertySimple meaningExample
AnonymityAn action cannot be linked to your real identity.E-voting — nobody should learn who you voted for.
IndistinguishabilityYou cannot be picked out of the crowd in a dataset.AOL 2006 — people were picked out of "anonymous" search logs.
Exam favourite: "Someone was re-identified from an anonymised dataset. Which property broke?" → Indistinguishability.
4 How to protect each property

One defence per property. Know this mapping cold — it is a classic multi-choice question.

DefenceProtectsHow
Encryption + access controlsConfidentialityOnly people with the key or access rights can read the data.
Hashing + logsIntegrityCompare hash values or check logs to spot any change. One changed character → totally different hash.
BackupsAvailabilityIf data is lost or locked, restore it from the backup.
De-identificationAnonymity + IndistinguishabilityTechniques like k-anonymity and differential privacy hide who is who.

Hashing in one line: a function that turns any value into a unique "digest" — change the input a tiny bit and the digest changes completely.

5 Information systems & the 3 states of data

An information system (IS) network = software + hardware + Internet + IoT devices + users, all sharing data. Data lives in 3 states:

StateMeaningExample
At restStoredData sitting in a database.
In transitMovingData sent over the Internet.
In useBeing processedData open in an app on your laptop.
6 Weak points — 5 vulnerable components
ComponentMain attackFix
Modem / WiFiOS exploit; man-in-the-middle (attacker sits between you and the other side, reads or changes traffic).Keep OS updated, use a firewall, encrypt the WiFi network.
DatabaseSQL injection — attacker types malicious SQL into an input box and the database runs it.Encrypt stored data, de-identify shared data, access controls, monitoring.
ComputerMalware, ransomware, OS exploits (WannaCry).Antivirus, firewall, updates, backups.
IoT devicesDefault passwords, weak security → spying, botnets, entry point (casino fish tank).Change default passwords, update firmware, encrypt communication.
HumansSocial engineering & phishing — tricking people into giving info. Plus malicious insiders (Snowden).Security training, strong authentication, tech + vigilant people.

Firewall in one line: a barrier between your private network and the Internet — lets safe traffic in, keeps bad traffic out.

Why are humans the weak point? Low awareness, easy to phish, weak passwords, click bad links — and insiders are trusted, so their damage is huge.
7 Re-identification — 4 attack types

Re-identification = working out who someone is from "anonymous" data, then learning more about them. It breaks anonymity, indistinguishability and confidentiality.

AttackSimple meaningExample
InferenceLearn or predict facts about a person from the data.AOL: find one person's searches → read all their searches.
AmplificationLink public data with anonymised data to learn new facts.Match medical records to public social media profiles.
DifferencingCompare two query results; the difference exposes one person.Count diagnoses with target vs without → target's diagnosis.
ReconstructionRebuild original data from aggregated or synthetic data.Rebuild personal movement paths from aggregate mobility data.
Memory trick — I·A·D·R: "I Always Discover Records."
8 Auxiliary knowledge — what attackers use

Re-identification usually needs extra knowledge on top of the dataset. Three kinds:

TypeMeaningExamples
Unique identifiersOne value = one person.Email, phone number, medical record number.
Quasi-identifiersNot unique alone — unique when combined.Gender + date of birth + ZIP code.
Background knowledgeThings the attacker already knows about the target."Only she visited the clinic that day."
Key number: ~87% of the US population can be uniquely identified from just gender + date of birth + 5-digit ZIP (Sweeney, 2000).

Adversary = any person, group, organisation or government that wants to cause harm through cyber activity (NIST).

// SHOW YOUR WORKINGCalculations

Every number this course asks you to produce, with every line of working. The exam gives marks for the steps, not just the answer — so these are laid out the way you should write them on paper.

The six things you can be asked to compute H(X) entropy · H(X,Y) joint entropy · H(X|Y) conditional entropy · IG information gain · RIG relative information gain · k and l from a table. Everything else is reading a definition.
1 The log₂ toolkit — no calculator needed

Every probability in this course is a small fraction, so you only ever need a handful of logs. Three tools cover all of it.

Tool 1 — powers of two are exact

log₂(1/2) = −1  ·  log₂(1/4) = −2  ·  log₂(1/8) = −3  ·  log₂(1/16) = −4

Any probability that is 1 over a power of 2 gives a whole number. If every group in your table has size 1/2, 1/4 or 1/8 of the total, you never need a decimal at all.

Tool 2 — the division rule

log₂(a/b) = log₂ a − log₂ b

So log₂(3/10) = log₂3 − log₂10 = 1.585 − 3.322 = −1.737. Useful when the fraction is not a tenth.

Tool 3 — the tenths table

p0.10.20.30.40.50.60.70.80.9
log₂ p−3.322−2.322−1.737−1.322−1.000−0.737−0.515−0.322−0.152
−p·log₂ p0.3320.4640.5210.5290.5000.4420.3600.2580.137
Use the bottom rowIt already has the minus sign folded in, so H is simply the sum of those numbers. Adding positives is far harder to get wrong than juggling negatives.

Memory aid: doubling p subtracts exactly 1 from log₂ p. 0.1 → 0.2 → 0.4 → 0.8 reads −3.322, −2.322, −1.322, −0.322. Same for 0.3 → 0.6.

2 H(X) — entropy of one attribute
H(X) = − Σ p(x) · log₂ p(x)

Recipe

  1. Group the records by the value of X. Count each group.
  2. p = count ÷ total.
  3. Look up −p·log₂p for each group.
  4. Add them. That sum is H(X). Units: bits.

Worked — Alejandra, TM 1 (postcode)

Ten records. Postcode 2015 appears 3 times, 2016 six times, 2017 once.

p(2015) = 3/10 = 0.3  →  0.521
p(2016) = 6/10 = 0.6  →  0.442
p(2017) = 1/10 = 0.1  →  0.332
─────────────────────────
H(X) = 0.521 + 0.442 + 0.332 = 1.295 bits

Worked — Alejandra, TM 2 (medical code)

A appears 5 times, B four times, C once.

0.5 → 0.500  ·  0.4 → 0.529  ·  0.1 → 0.332
H(X) = 0.500 + 0.529 + 0.332 = 1.361 bits
Sanity rangeH is 0 when one value is certain, and maximum at log₂(number of distinct values) when they are all equally likely. Three values → H can never exceed log₂3 = 1.585. Both answers above sit under it. ✓
3 H(X,Y) — joint entropy of two attributes

When the adversary knows both attributes, one "value" is the pair. Nothing else about the method changes.

H(X,Y) = − Σ p(x,y) · log₂ p(x,y)

Recipe

  1. Write out the pair for every record: (postcode, medical code).
  2. Count identical pairs. These counts must still total the number of records.
  3. Same formula, same table, same sum.

Then run the two checks

  1. Marginal check. Add the pair counts down one attribute — you must recover the single-attribute counts exactly. Do it in both directions.
  2. Bound check. max(H(X), H(Y)) ≤ H(X,Y) ≤ H(X) + H(Y) — adding an attribute can only raise uncertainty or leave it unchanged, never lower it.
Known error in the circulated exampleThe TM 3 pairs (2015,A) 3 · (2016,A) 3 · (2016,B) 3 · (2017,C) 1 giving 1.895 bits fail the marginal check — they imply A = 6, B = 3, while TM 2 states A = 5, B = 4. The lecturer has confirmed this is a mistake. Note that 1.895 passes the bound check, so only the marginal check catches it. Recompute from the corrected pairs.
4 H(X|Y) — conditional entropy, the one everyone skips

This is the step the IG formula depends on, and it is where marks are lost. H(X|Y) asks: once the adversary has seen the de-identified value Y, how much uncertainty about the real X is left?

H(X|Y) = Σ p(y) · H(X | Y = y)

In words: for each possible Y, work out the entropy of X within that group only, then average those, weighted by how common each Y is.

Recipe

  1. Group the records by Y — one group per de-identified value.
  2. For each group, find p(y) = group size ÷ total.
  3. Inside that group, look at the original X values and compute H(X|Y=y) using the ordinary entropy formula, but with probabilities within the group.
  4. Multiply each group's entropy by its p(y), and add.
The shortcut worth knowingIf a group contains only one distinct original value, its entropy is 0 — that record is fully exposed and contributes nothing. Only mixed groups add uncertainty. This is often most of the work done in one glance.

Method 2 — the chain rule

H(X|Y) = H(X,Y) − H(Y)

Use whichever is faster, and if you have time use both — they must agree, which makes it a free check on your own arithmetic.

5 IG and RIG — worked end to end
IG = H(X) − H(X|Y)      RIG = IG / H(X)

IG = how many bits about the original data the adversary gains by seeing the de-identified version. Lower is safer. RIG rescales it to 0–1 so it lines up with the privacy spectrum.

The slide's figures are under correctionThe lecturer has confirmed an error in the printed encoding example. The method below is unaffected — but the final number depends entirely on how many records carry each name, so both readings are worked out here. Bring the corrected record list and you can read your answer straight off.

The setup is the same in both: names are encoded phonetically, so Peter and Pete collapse to p360, and the Smith family collapses to s530.

Reading A — one name repeats: Smith, Smith, Peter, Pete

#X — real nameY — code
1Smiths530
2Smiths530
3Peterp360
4Petep360

Step 1 — H(X). Smith 2/4, Peter 1/4, Pete 1/4:

H(X) = 0.5·1 + 0.25·2 + 0.25·2 = 0.5 + 0.5 + 0.5 = 1.5 bits

Step 2 — H(X|Y). Group by code and measure what is left inside each group:

Yp(y)X inside the groupH(X|Y=y)
s5302/4 = 0.5Smith, Smith — one value only0 bits
p3602/4 = 0.5Peter, Pete — 50 / 501 bit
H(X|Y) = 0.5 × 0 + 0.5 × 1 = 0.5 bits

Step 3 — IG and RIG.

IG  = 1.5 − 0.5 = 1.0 bit
RIG = 1.0 / 1.5 = 0.67

Reading B — four distinct names: Peter, Pete, Smith, Smyth

Same two codes, but now every name is different, so nothing was repeated to begin with.

Step 1 — H(X). Four equally likely names, 1/4 each:

H(X) = 4 × (0.25 × 2) = 2.0 bits

Step 2 — H(X|Y). Now both groups are mixed:

Yp(y)X inside the groupH(X|Y=y)
p3602/4 = 0.5Peter, Pete — 50 / 501 bit
s5302/4 = 0.5Smith, Smyth — 50 / 501 bit
H(X|Y) = 0.5 × 1 + 0.5 × 1 = 1.0 bit

Step 3 — IG and RIG.

IG  = 2.0 − 1.0 = 1.0 bit
RIG = 1.0 / 2.0 = 0.50

Why they differ — and which one the slide probably means

ReadingH(X)H(X|Y)IGRIGSpectrum label
A · Smith repeats1.50.51.00.67between moderate and high
B · four distinct2.01.01.00.50exactly "moderate"
The tellBoth readings leak the same 1 bit — that part is stable. What changes is the baseline H(X) you divide by. The printed slide states 0.67 but describes the result as "suspicion with moderate confidence", and moderate is the 0.50 mark on the spectrum. The number and its own label disagree, which is consistent with the error the lecturer flagged. Reading B is self-consistent; check the record list on the slide to confirm.

The last step is a sentence, not a number

Whichever value you get, the marks are in the interpretation:

  • RIG = 0 → the de-identified data reveals nothing about the original. Perfect privacy.
  • RIG = 0.5 → half the information survives. Suspicion with moderate confidence.
  • RIG = 1 → everything survives. The de-identification did nothing at all.
The point the example is makingPhonetic encoding is weak de-identification. It hides the difference between Peter and Pete, but it hides nothing else — and if a name appears in only one record, that record stays fully exposed. That conclusion holds under both readings.
6 Reading k and l off a table

No formula here — it is a counting exercise, and the trick is knowing what to count.

Recipe

  1. Identify the QID columns (the quasi-identifiers — gender, birth year, postcode). Ignore the sensitive column for now.
  2. Group rows that are identical across all QID columns. These groups are called equivalence classes.
  3. k = the size of the smallest group.
  4. Inside each group, count the distinct sensitive values. l = the smallest of those counts.

Worked

GenderBornPostcodeDiagnosis
F198550200flu
F198550200flu
F198550200cough
M199050100asthma
M199050100flu
M199050100asthma

Two groups, three rows each. Group F has diagnoses {flu, cough} → 2 distinct. Group M has {asthma, flu} → 2 distinct.

k = min(3, 3) = 3  → the table is 3-anonymous
l = min(2, 2) = 2  → the table is 2-diverse
The trapk counts rows per group; l counts distinct sensitive values inside a group. Both are the minimum across groups — one bad group drags the whole table down. A table can be 5-anonymous and still 1-diverse, which leaks the diagnosis of everyone in that group.
7 Exam checklist
  1. State the formula before you use it. It is usually worth a mark on its own.
  2. Show p for every group. Write "3/10 = 0.3", not just 0.3.
  3. Keep 3 decimal places through the working, round only at the end.
  4. Write the unit — bits. Entropy, IG and joint entropy are all in bits. RIG has no unit; it is a ratio between 0 and 1.
  5. Check the probabilities sum to 1 before computing anything.
  6. Run the marginal check on any joint table. Ten seconds, and it catches the error class the lecturer just confirmed.
  7. Sanity-check the size: 0 ≤ H ≤ log₂(distinct values), and H(X,Y) ≥ max(H(X), H(Y)).
  8. Finish with a sentence. "RIG = 0.50 → moderate risk, so this de-identification is too weak to publish." The interpretation is what the question is really testing.
Most common lost marksForgetting the minus sign · using log₁₀ instead of log₂ · computing H(X|Y) as a plain average instead of weighting by p(y) · dividing RIG by H(Y) instead of H(X) · giving a number with no interpretation.

// REAL WORLDCase Files

Eleven breaches straight from the slides. For each one know three things: what happened, which property broke, and the lesson. Exam questions are usually one of those three.

📘 Facebook / Cambridge Analytica

2018
Data breach Third-party API

50–90 million users exposed through a loophole in Facebook's API used by a third-party quiz app. Facebook knew about the issue as early as 2015 and did nothing.

Lesson: ignoring known flaws is expensive — Facebook paid a $5 billion fine for poor data protection.

🦠 WannaCry ransomware

2017
Confidentiality Availability

Infected 250,000+ computers in 150 countries using a Windows vulnerability. Files were hijacked and encrypted; victims had to pay $300 in bitcoin for the key.

Lesson: unpatched systems are open doors — keep the OS updated, keep backups.

⚡ GitHub DDoS

2018
Availability

A flood from 1,000+ systems peaked at 1.35 terabits/second126.9 million packets per second — knocking GitHub offline and then intermittent for a short time.

Lesson: a DoS attack breaks availability — overwhelm the service, real users get locked out.

🔎 AOL search logs

2006
Indistinguishability Inference attack

AOL released "anonymised" search histories of 650,000 users for research. People who searched their own name, SSN, phone or location were re-identified — exposing all their searches.

Lesson: removing names is not anonymisation. Searches themselves identify you.

🎬 Netflix prize data

2008
Re-identification

Anonymised movie ratings of 500,000 subscribers were released for a contest. Researchers (Narayanan & Shmatikov) re-identified people by linking ratings with public IMDb reviews.

Lesson: linking two datasets (an amplification-style attack) beats weak anonymisation.

🏥 Massachusetts hospital data

1997
Quasi-identifiers

Sweeney re-identified people (famously the state governor) in "anonymous" hospital discharge data by linking it with voter records using ZIP + birth date + gender.

Lesson: quasi-identifiers combine into a fingerprint — ~87% of the US is unique on gender + DOB + ZIP.

📡 Optus breach

2022
Confidentiality Insecure API

An exposed API that required no authentication let attackers pull 10+ million customer records — names, addresses, birth dates, passport and Medicare numbers. Data of 10,000 customers hit the dark web; a class action followed in 2023.

Lesson: every public endpoint must demand authentication. One open API = the whole database.

🏥 Medibank breach

2022
Confidentiality Stolen credentials

An employee's stolen credentials gave attackers access to 200GB of data on 9.7 million customers, including medical claims. APRA made Medibank set aside $250 million.

Lesson: one set of stolen credentials can open everything — use multi-factor authentication.

🐠 Casino fish tank

2017
IoT entry point

Hackers entered a casino's network through an Internet-connected fish-tank thermometer, then scanned for weaknesses and stole 10GB of data.

Lesson: the weakest connected device sets your security level — even a thermometer.

🕵️ Edward Snowden

2013
Insider threat

An IT administrator at an NSA contractor downloaded thousands of top-secret files in May 2013 and released them to journalists in June 2013.

Lesson: insiders are trusted, so their damage is huge. Access control and monitoring matter inside too.

📷 Nest camera

Default password

A smart camera left on its default password can be taken over to spy on the owner and infer private information.

Lesson: never keep default passwords on IoT devices.

// TRAININGDrills

Thirty-six cards to make the definitions automatic, then a twenty-question mock exam that explains every answer — including the discussion questions lifted straight from the slides.

Flashcards

Click the card to flip it. Be honest — mark "Again" if you hesitated. Keyboard: Space flip · ←/→ move · G got it · A again.

Topic 1
click to flip
click to flip back

Mock exam

Twenty questions. Single-answer ones grade the moment you click; multi-answer ones wait for Check.

// LAST LOOKCheat Sheet

Everything squeezed onto one screen. This is the page to read in the ten minutes before you walk in.

T1 The 5 properties

PropertyMeaningBroken byDefended by
ConfidentialityOnly the right people see itBreaches, ransomware, insidersEncryption, access controls
IntegrityData not changed, trustworthyModified data, phishing, fake newsHashing, logs
AvailabilityReachable when neededDoS / DDoSBackups
AnonymityAction can't be linked to youRe-identificationDe-identification
IndistinguishabilityCan't be picked from the crowdRe-identificationDe-identification (k-anonymity, differential privacy)

T1 4 re-identification attacks

InferenceLearn/predict facts about a person from the data (AOL).
AmplificationLink public data + anonymised data → new facts.
DifferencingQuery with target vs without → difference exposes them.
ReconstructionRebuild original data from aggregated/synthetic data.

Fuel: unique identifiers · quasi-identifiers · background knowledge. Key stat: 87% of US unique on gender + DOB + 5-digit ZIP.

T1 Component → attack → fix

Modem/WiFiOS exploit, man-in-the-middleUpdates, firewall, encrypt WiFi
DatabaseSQL injectionEncrypt, de-identify, monitor
ComputerMalware, ransomwareAntivirus, updates, backups
IoTDefault passwords, botnetsChange passwords, update, encrypt
HumansPhishing, social engineering, insidersTraining, strong authentication

T2 4 risky elements → defence models

UniquenessRare values stand out (Jenjira vs John)k-anonymity: each value combo shared by ≥ k records
UniformityRepeated values reveal a personl-diversity: ≥ l different sensitive values per group
Linkable tokensPII hiding in the data (emails in free text)→ remove/mask direct identifiers
CorrelationOne attribute predicts another; links datasets→ break correlations, perturb values

T2 Threat models & spectrum

Threat models① single attribute < ② combination of attributes · ③ direct identifiers extracted + linked externally. More knowledge = more risk.
Privacy spectrum0.0 absolute privacy · 0.25 low suspicion · 0.5 moderate · 0.75 high · 1.0 provably exposed

T2 Formulas & key numbers

H(X) = − Σ p(x)·log₂ p(x)   ·   IG = H(X) − H(X|Y)   ·   RIG = IG / H(X)
Fair coin 0.5/0.51 bit (max uncertainty)Certain outcome 1.0/0.00 bits
Coin 0.6/0.40.971 bitsCoin 0.8/0.20.722 bits
Alejandra TM1 (postcode)1.295 bitsTM2 (medical code)1.361 bits
TM3 (both attributes)highest risk — recompute from the pairs
printed 1.895 is a known error
Encoding example RIG0.50 or 0.67
slide under correction — see Calc §5

Read: higher entropy = more unique values = more info for the attacker = higher re-ID risk. Lower IG = better de-identification.

📁 Cases in one line each

Facebook/CA '18Quiz-app API loophole, 50–90M users, $5B fine
WannaCry '17Windows flaw, 250k PCs, 150 countries, $300 BTC ransom
GitHub '18DDoS 1.35 Tbps — availability down
AOL '06650k "anonymous" search logs → users re-identified
Netflix '08500k subscribers re-identified by linking with IMDb
Massachusetts '97Governor re-identified via ZIP+DOB+gender
Optus '22API with no authentication, 10M+ records
Medibank '22Stolen credentials, 200GB, 9.7M customers, $250M
Casino '17Fish-tank thermometer → 10GB stolen
Snowden '13Insider downloaded top-secret NSA files

⚡ Last-minute answers

  • Ransomware → confidentiality broken (attacker controls the data) and availability (you lose access).
  • Insider selling secrets → confidentiality.
  • Re-identified from an anonymised dataset → indistinguishability.
  • Integrity is preserved by hashing + logs (not encryption, not backups).
  • Social networking, location, transaction and web-search data are all sensitive.
  • k = 5 → every record looks like at least 4 others.
  • 3 states of data: at rest · in transit · in use.

// WHO BUILT THISZann

One of four course consoles I built for this semester. Same shell, same keyboard shortcuts, same offline-first rule — open the file and it works, with the Wi-Fi off.

Author

NameThu Htoo Zan — Zann
Student ID670615524
ProgrammeInformation Systems & Network Engineering (ISNE)
FacultyFaculty of Engineering, Chiang Mai University

This course

CodeISNE 269497
TitleThe Cult of Privacy Technologies
TermSemester 1, 2026
InstructorAsst. Prof. Jenjira Jaimunk, PhD
ClassTue & Fri 13:00–14:30 · Room 601, Engineering Building
Office hoursMonday 09:00–12:00
AssessmentHomework & quizzes 30% · Midterm 35% · Final 35%
On the contentThese notes are my own restatement of the course material, written for my own revision. Lecture slides and figures remain the property of the instructor and are not redistributed here.

Built with

Vanilla HTML, CSS and JavaScript. No framework, no build step, no dependencies, no network calls — one file you can open from a USB stick. Progress, theme and your place in each section are stored in localStorage on your own device and nowhere else.

ZZANN