Case Study
degauss
Measures your identity exposure in bits, maps the data broker supply chain, and computes the optimal order to remove yourself from the internet.
TypeScriptInformation TheoryFellegi-SunterGraph TheoryTor SOCKS5Shannon EntropyJaro-WinklerEdmonds-Karp
01 / Engineering
What it actually does
Not a score from 1 to 10. Bits of information, measured and sourced.
02 / The Code
From the source
Entropy primitives, record linkage weights, and removal optimisation. Copied from the repo.
Shannon Entropy
H(X) = -Σ p(x) log₂ p(x). Each quasi-identifier contributes bits of exposure.
/** Shannon entropy of a probability distribution (bits).
* H(X) = -Σ p(x) log₂ p(x)
* Returns 0 for empty or degenerate distributions. */
export function shannonEntropy(probs: number[]): number {
let h = 0;
for (const p of probs) {
if (p > 0 && p <= 1) h -= p * Math.log(p) / LN2;
}
return h;
}
/** Self-information (surprisal) of a specific value.
* I(x) = -log₂ p(x)
* A name shared by 1 in 10,000: ~13.3 bits.
* A name shared by 1 in 10: ~3.3 bits. */
export function selfInfo(frequency: number): number {
if (frequency <= 0 || frequency >= 1) return 0;
return -Math.log(frequency) / LN2;
}Fellegi-Sunter Field Weight
Log-likelihood ratio: agreement on a rare field (low u) gives a high positive weight.
/** Compute the Fellegi-Sunter linkage weight for a single field. */
export function fieldWeight(
field: QIField,
agrees: boolean,
uOverride?: number
): FieldComparison {
const m = M_PROB[field] ?? M_PROB.other;
const u = uOverride ?? estimateFrequency(field);
let weight: number;
if (agrees) {
// w(agree) = log₂(m / u)
// agreement on a rare field gives a high positive weight
weight = Math.log2(m / Math.max(u, 1e-15));
} else {
// w(disagree) = log₂((1-m) / (1-u))
weight = Math.log2((1 - m) / Math.max(1 - u, 1e-15));
}
return { field, agrees, weight, mProb: m, uProb: u };
}Greedy Removal Ordering
Sort attributes by efficiency: bits reduced per unit of removal difficulty.
/** Greedy removal ordering: sort by bits/difficulty.
*
* If the exposure function is submodular (diminishing returns),
* greedy achieves (1-1/e) ≈ 63% of optimal.
* Submodularity is NOT proven here — the guarantee is aspirational.
* Krause & Golovin, "Submodular Function Maximization" (2014). */
export function greedyRemovalOrder(
attributes: Array<{ bits: number; difficulty: number }>
): number[] {
const indices = attributes.map((_, i) => i);
indices.sort((a, b) => {
const effA = attributes[a].bits / Math.max(attributes[a].difficulty, 0.01);
const effB = attributes[b].bits / Math.max(attributes[b].difficulty, 0.01);
return effB - effA;
});
return indices;
}0
lines of TypeScript
0
tests passing
0
attack scenarios
0
broker nodes