Browser Fingerprint Viewer — Go source
See exactly what websites can learn about your browser without cookies — screen, GPU, fonts, timezone, language, and more. Educational, not tracking.
This is the Go implementation — the same logic the interactive tool runs, in a shareable, citable form.
// browser-fingerprint — Go polyglot showcase port (pure analysis layer).
//
// The authoritative Go source is the live CLI twin at
// cli/browser-fingerprint/browserfingerprint.go — the foundation of the
// `cosmodev browser-fingerprint` command, kept in lock-step with
// src/lib/browser-fingerprint.ts via shared test vectors. This display
// snippet mirrors that twin's portable core: the signal registry, risk
// classification, category grouping, risk counts, and the SHA-256
// fingerprint hash. The browser-API collection layer (canvas / WebGL / font
// probes) is DOM-bound by design and does not port.
//
// Display source — part of CosmoDev's polyglot tool pages.
package browserfingerprint
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"strings"
)
// RiskLevel is a privacy-risk classification:
// - high: near-unique on its own or part of a rare combination
// - medium: narrows you to a sizable-but-specific population
// - low: coarse; shared by millions of browsers
type RiskLevel string
const (
RiskLow RiskLevel = "low"
RiskMedium RiskLevel = "medium"
RiskHigh RiskLevel = "high"
)
// FingerprintCategory groups related signals.
type FingerprintCategory string
const (
CategoryHardware FingerprintCategory = "Hardware"
CategoryGraphics FingerprintCategory = "Graphics"
CategoryNetwork FingerprintCategory = "Network"
CategoryBrowser FingerprintCategory = "Browser"
CategoryInput FingerprintCategory = "Input"
)
// FingerprintCategories is the canonical category order (card grid, top to
// bottom).
var FingerprintCategories = []FingerprintCategory{
CategoryHardware, CategoryGraphics, CategoryNetwork, CategoryBrowser, CategoryInput,
}
// FingerprintSignal is one collected, classified signal.
type FingerprintSignal struct {
ID string
Label string
Value string
Risk RiskLevel
Category FingerprintCategory
}
// SignalDef classifies one signal id.
type SignalDef struct {
Label string
Risk RiskLevel
Category FingerprintCategory
}
// SignalDefinitions is the static registry of every signal the tool
// collects, with its privacy-risk classification. Single source of truth:
// BuildSignal derives from it, so a signal can never be collected without
// being classified.
var SignalDefinitions = map[string]SignalDef{
// High risk
"canvas": {Label: "Canvas fingerprint", Risk: RiskHigh, Category: CategoryGraphics},
"webgl-renderer": {Label: "WebGL renderer", Risk: RiskHigh, Category: CategoryGraphics},
"webgl-vendor": {Label: "WebGL vendor", Risk: RiskHigh, Category: CategoryGraphics},
"fonts": {Label: "Installed fonts", Risk: RiskHigh, Category: CategoryBrowser},
"timezone": {Label: "Timezone", Risk: RiskHigh, Category: CategoryBrowser},
// Medium risk
"screen": {Label: "Screen & color depth", Risk: RiskMedium, Category: CategoryHardware},
"device-pixel-ratio": {Label: "Device pixel ratio", Risk: RiskMedium, Category: CategoryHardware},
"hardware-concurrency": {Label: "CPU cores", Risk: RiskMedium, Category: CategoryHardware},
"device-memory": {Label: "Device Memory", Risk: RiskMedium, Category: CategoryHardware},
"platform": {Label: "Platform", Risk: RiskMedium, Category: CategoryBrowser},
"languages": {Label: "Languages", Risk: RiskMedium, Category: CategoryBrowser},
"touch": {Label: "Touch support", Risk: RiskMedium, Category: CategoryInput},
"connection": {Label: "Connection type", Risk: RiskMedium, Category: CategoryNetwork},
// Low risk
"user-agent": {Label: "User agent", Risk: RiskLow, Category: CategoryBrowser},
"do-not-track": {Label: "Do Not Track", Risk: RiskLow, Category: CategoryBrowser},
"cookies-enabled": {Label: "Cookies enabled", Risk: RiskLow, Category: CategoryBrowser},
"online": {Label: "Online status", Risk: RiskLow, Category: CategoryNetwork},
"pdf-viewer": {Label: "PDF viewer", Risk: RiskLow, Category: CategoryBrowser},
}
// ClassifySignalRisk returns the risk level for a known signal id; ok is
// false for unknown ids.
func ClassifySignalRisk(id string) (RiskLevel, bool) {
def, ok := SignalDefinitions[id]
return def.Risk, ok
}
// BuildSignal builds a signal from its id and collected value. Errors on
// unknown ids so a typo'd id fails loudly instead of silently rendering an
// unclassified row.
func BuildSignal(id string, value string) (FingerprintSignal, error) {
def, ok := SignalDefinitions[id]
if !ok {
return FingerprintSignal{}, fmt.Errorf("Unknown fingerprint signal id: %s", id)
}
return FingerprintSignal{ID: id, Label: def.Label, Value: value, Risk: def.Risk, Category: def.Category}, nil
}
// CategoryGroup is one category and its signals.
type CategoryGroup struct {
Category FingerprintCategory
Signals []FingerprintSignal
}
// GroupByCategory groups signals by category in canonical order, omitting
// empty categories.
func GroupByCategory(signals []FingerprintSignal) []CategoryGroup {
groups := map[FingerprintCategory][]FingerprintSignal{}
for _, s := range signals {
groups[s.Category] = append(groups[s.Category], s)
}
var out []CategoryGroup
for _, c := range FingerprintCategories {
if list, ok := groups[c]; ok {
out = append(out, CategoryGroup{Category: c, Signals: list})
}
}
return out
}
// HashFingerprint is the stable fingerprint ID: SHA-256 over every signal
// value, joined in signal order. Same browser state → same hash; any single
// changed value → new hash.
func HashFingerprint(signals []FingerprintSignal) string {
return Sha256Hex(ConcatSignalValues(signals))
}
// ConcatSignalValues is the exact byte string the fingerprint hash is
// computed over: every value joined with | in signal order.
func ConcatSignalValues(signals []FingerprintSignal) string {
values := make([]string, len(signals))
for i, s := range signals {
values[i] = s.Value
}
return strings.Join(values, "|")
}
// Sha256Hex is the SHA-256 hex digest of input.
func Sha256Hex(input string) string {
sum := sha256.Sum256([]byte(input))
return hex.EncodeToString(sum[:])
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →