Aethel Noise

The internet has been talking to itself for fifty years. Nobody kept a recording.

I. How it works

Aethel Noise – the ears and the voice of network chaos. Every network constantly produces noise — jitter, timing drift, queueing artefacts, entropy nobody keeps. We are building the instrument that listens to it, the dataset that describes it, and possibly a way to speak through it without sending anything.

We treat noise as a signal, not as an error

Network engineering spends its life removing noise. We do the opposite: we record it. Round-trip time variance, inter-arrival jitter, retransmission timing, path asymmetry, congestion micro-oscillations — the residue left after the useful payload is stripped away.

The program began as a stubborn question: can raw network noise be used as a computational substrate? It can. We built a working noise-driven transistor, and then a calculator on top of it. It is slow, ugly and entirely useless for computing — and that was the finding that mattered. If noise can gate a switch, it carries structure. And structure that carries information is worth listening to rather than computing with.

So Aethel Noise turned from a computer into an instrument: a distributed sensor network whose only job is to hear the internet breathing, and to archive that breathing in a form an AI can study.

II. Research Areas

1. Aethel Noise Sensor.
A distributed listener that records multiple classes of network noise, at high time resolution, from many vantage points, continuously — and stores it as an analysable corpus. The point is data: raw, labelled, long-baseline observation nobody currently keeps.

2. Aethel Noise Protocol.
The ambitious half. Two identical programs on opposite sides of the planet, sharing an algorithm and no channel. Both listen to the same statistical weather of the network. From patterns in that weather they agree who transmits and who receives, when, and how the symbol is encoded. No packet is sent by either side. Communication as coordinated observation.

We state plainly that the Protocol may fail. The Sensor and its dataset are valuable regardless.

III. Expected results

1. Data that literally nobody has
Long-baseline, multi-point, high-resolution recordings of network noise as a first-class object of study. Not traffic captures — noise. This is the core asset and it appreciates every day it runs.

2. A working sensor product
Deployable instrumentation with obvious applications in network forecasting, anomaly and outage prediction, infrastructure monitoring and security telemetry — value independent of the research thesis.

3. Noise patterns and their meaning
Recurring signatures tied to real-world events: routing changes, outages, congestion waves, load cycles, physical infrastructure behaviour. A grammar of network chaos, discovered by AI analysis over the corpus.

4. If we are lucky: untraceable transmission
A method of conveying information that produces no packet to intercept, because nothing is sent. High risk, and a fundamental result in communication theory if it holds.

IV. Already done

Prototypes that exist and run

Early, rough, single-author code. Shown because it is real, not because it is finished. Expand a section to read it.

Prototype 1 · Network-noise transistor

The gate is driven by jitter entropy rather than voltage. A sample above the adaptive threshold conducts; below it, the channel is closed.


import time, socket, statistics

class NoiseSource:
    """Samples raw timing noise from a network path."""
    def __init__(self, host="1.1.1.1", port=80, window=64):
        self.host, self.port, self.window = host, port, window
        self.history = []

    def sample(self):
        t0 = time.perf_counter_ns()
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(1.0)
        try:
            s.connect_ex((self.host, self.port))
        finally:
            s.close()
        rtt = time.perf_counter_ns() - t0
        self.history.append(rtt)
        if len(self.history) > self.window:
            self.history.pop(0)
        return rtt

    def jitter(self):
        if len(self.history) < 4:
            return 0.0
        d = [abs(b - a) for a, b in zip(self.history, self.history[1:])]
        return statistics.fmean(d)


class NoiseTransistor:
    """Gate conducts when noise entropy crosses an adaptive threshold."""
    def __init__(self, source, bias=1.0):
        self.src = source
        self.bias = bias
        self.threshold = None

    def _calibrate(self):
        j = [self.src.jitter() for _ in range(16) if self.src.sample()]
        j = [x for x in j if x > 0]
        self.threshold = statistics.median(j) * self.bias if j else 1.0

    def conduct(self, gate=True):
        if self.threshold is None:
            self._calibrate()
        self.src.sample()
        if not gate:
            return 0
        return 1 if self.src.jitter() > self.threshold else 0


if __name__ == "__main__":
    t = NoiseTransistor(NoiseSource())
    print("noise-gated output:", [t.conduct() for _ in range(20)])

		 
Prototype 2 · Calculator running on network noise

Gates are built from noise transistors, then composed into a ripple-carry adder. Correctness is fully deterministic, yet the execution time is bound to physical entropy: every gate blocks and polls the noise transistor dynamically until it stabilizes and confirms the required quantum state. It works. It is roughly ten million times slower than a pocket calculator, which is precisely the result that redirected the whole program towards listening.


from noise_transistor import NoiseTransistor, NoiseSource

class NoiseLogic:
    def __init__(self):
        self.t = NoiseTransistor(NoiseSource())
        # Global counter to track total transistor state samples across all operations
        self.total_samples = 0

    def _wait_for_gate_signal(self, expected_conduct):
        """
        Physical synchronization loop. Continually samples the noise transistor 
        until it physically confirms the required state (conduction or isolation).
        """
        while True:
            self.total_samples += 1
            # Sample the actual real-time state of the noise transistor
            current_state = 1 if self.t.conduct() else 0
            if current_state == expected_conduct:
                return current_state

    def NOT(self, a):
        # 0 -> wait until the transistor opens (1)
        # 1 -> wait until the transistor closes (0)
        target = 1 - a
        return self._wait_for_gate_signal(target)

    def AND(self, a, b):
        # Current flows only if both inputs are active AND the transistor conducts
        if a == 1 and b == 1:
            return self._wait_for_gate_signal(1)
        return self._wait_for_gate_signal(0)

    def OR(self, a, b):
        # Current flows if at least one input is active AND the transistor conducts
        if a == 1 or b == 1:
            return self._wait_for_gate_signal(1)
        return self._wait_for_gate_signal(0)

    def XOR(self, a, b):
        # Classic exclusive OR gating driven by the stabilized noise switch
        if a != b:
            return self._wait_for_gate_signal(1)
        return self._wait_for_gate_signal(0)

    def half_adder(self, a, b):
        return self.XOR(a, b), self.AND(a, b)

    def full_adder(self, a, b, cin):
        s1, c1 = self.half_adder(a, b)
        s2, c2 = self.half_adder(s1, cin)
        return s2, self.OR(c1, c2)

    def add(self, x, y, width=8):
        carry, out = 0, 0
        for i in range(width):
            a, b = (x >> i) & 1, (y >> i) & 1
            bit, carry = self.full_adder(a, b, carry)
            out |= bit << i
        return out


if __name__ == "__main__":
    calc = NoiseLogic()
    
    # Perform the calculation
    result = calc.add(13, 29)
    
    # Print the absolute result and stochastic efficiency stats
    print("--- Aethel Stochastic Calculator Execution ---")
    print(f"13 + 29 = {result}")
    print(f"Total transistor samples processed: {calc.total_samples}")
    print("----------------------------------------------")



		 
Prototype 4 · Ramdom function on network noise

Physical entropy source based on network timing noise (jitter, queueing artifacts, scheduler drift), with conservative entropy accounting and a cryptographic extractor.

Design principles:
1. Raw network timings are NOT uniform. They are debiased and conditioned.
2. Entropy is accounted conservatively (min-entropy, NIST SP 800-90B style).
3. Network entropy is ALWAYS mixed with os.urandom(), so output quality is
never worse than the system CSPRNG, even under a hostile network.
4. Health tests (repetition count, adaptive proportion) gate the source.


from __future__ import annotations

import hashlib
import hmac
import math
import os
import socket
import statistics
import struct
import time
from collections import Counter
from dataclasses import dataclass, field


# ---------------------------------------------------------------------------
# 1. Raw noise sampling
# ---------------------------------------------------------------------------

@dataclass
class NoiseSource:
    """Samples raw timing noise from one or more network paths.

    Multiple targets are rotated so that a single congested or adversarial
    path cannot dominate the entropy pool.
    """

    targets: tuple = (
        ("1.1.1.1", 80),
        ("8.8.8.8", 53),
        ("9.9.9.9", 53),
        ("208.67.222.222", 53),
    )
    window: int = 256
    timeout: float = 1.0

    history: list = field(default_factory=list)
    _idx: int = 0
    samples_taken: int = 0

    def sample(self) -> int:
        """One TCP-connect attempt; returns RTT in nanoseconds."""
        host, port = self.targets[self._idx % len(self.targets)]
        self._idx += 1

        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(self.timeout)
        t0 = time.perf_counter_ns()
        try:
            s.connect_ex((host, port))
        except OSError:
            pass
        finally:
            rtt = time.perf_counter_ns() - t0
            s.close()

        self.history.append(rtt)
        if len(self.history) > self.window:
            self.history.pop(0)
        self.samples_taken += 1
        return rtt

    def jitter(self) -> float:
        """Mean absolute successive difference — the classic jitter metric."""
        if len(self.history) < 4:
            return 0.0
        d = [abs(b - a) for a, b in zip(self.history, self.history[1:])]
        return statistics.fmean(d)


# ---------------------------------------------------------------------------
# 2. Bit extraction from raw timings
# ---------------------------------------------------------------------------

class BitExtractor:
    """Turns noisy integers into a debiased bit stream.

    Two stages:
      * LSB slicing  - keep only the low bits of the *difference* between
                       consecutive samples. Deterministic structure (base
                       latency, path length) lives in the high bits; the low
                       bits are dominated by the noise floor.
      * Von Neumann  - consume bit pairs, emit 0 for (0,1) and 1 for (1,0),
                       discard (0,0)/(1,1). Removes static bias at the cost
                       of throughput. Bias-free for i.i.d. input.
    """

    def __init__(self, lsb_bits: int = 8):
        self.lsb_bits = lsb_bits
        self.mask = (1 << lsb_bits) - 1
        self._prev: int | None = None
        self._pair: int | None = None
        self.raw_bits_in = 0
        self.bits_out = 0

    def feed(self, rtt_ns: int) -> list[int]:
        """Feed one raw sample, get zero or more debiased bits back."""
        if self._prev is None:
            self._prev = rtt_ns
            return []

        delta = abs(rtt_ns - self._prev)
        self._prev = rtt_ns

        out: list[int] = []
        for i in range(self.lsb_bits):
            bit = (delta >> i) & 1
            self.raw_bits_in += 1
            if self._pair is None:
                self._pair = bit
            else:
                a, b = self._pair, bit
                self._pair = None
                if a != b:
                    out.append(a)          # von Neumann
                    self.bits_out += 1
        return out

    @property
    def efficiency(self) -> float:
        if not self.raw_bits_in:
            return 0.0
        return self.bits_out / self.raw_bits_in


# ---------------------------------------------------------------------------
# 3. Health tests (NIST SP 800-90B continuous tests)
# ---------------------------------------------------------------------------

class HealthTests:
    """Detects catastrophic source failure — stuck source, hard bias."""

    def __init__(self, rep_cutoff: int = 32, prop_window: int = 512,
                 prop_cutoff: float = 0.65):
        self.rep_cutoff = rep_cutoff
        self.prop_window = prop_window
        self.prop_cutoff = prop_cutoff
        self._last: int | None = None
        self._run = 0
        self._buf: list[int] = []
        self.failures = 0

    def update(self, bit: int) -> bool:
        """Returns True if the source currently looks healthy."""
        ok = True

        # Repetition count test: identical value repeated too many times.
        if bit == self._last:
            self._run += 1
            if self._run >= self.rep_cutoff:
                ok = False
        else:
            self._last, self._run = bit, 1

        # Adaptive proportion test: one symbol dominating the window.
        self._buf.append(bit)
        if len(self._buf) >= self.prop_window:
            ones = sum(self._buf)
            frac = max(ones, len(self._buf) - ones) / len(self._buf)
            if frac > self.prop_cutoff:
                ok = False
            self._buf.clear()

        if not ok:
            self.failures += 1
        return ok


# ---------------------------------------------------------------------------
# 4. Conservative entropy estimation
# ---------------------------------------------------------------------------

def min_entropy_per_bit(bits: list[int]) -> float:
    """Min-entropy of the empirical distribution: -log2(max p_i).

    Deliberately pessimistic. Reports 0.0 for short samples so the pool
    never over-credits itself.
    """
    n = len(bits)
    if n < 128:
        return 0.0
    c = Counter(bits)
    p_max = max(c.values()) / n
    return -math.log2(p_max)


def markov_min_entropy(bits: list[int]) -> float:
    """First-order Markov min-entropy estimate.

    Network noise is strongly autocorrelated (RTT_t depends on RTT_{t-1}).
    An IID estimate would flatter the source; this penalises that structure.
    """
    if len(bits) < 256:
        return 0.0
    trans: Counter = Counter(zip(bits, bits[1:]))
    worst = 0.0
    for state in (0, 1):
        total = trans[(state, 0)] + trans[(state, 1)]
        if total == 0:
            continue
        p = max(trans[(state, 0)], trans[(state, 1)]) / total
        worst = max(worst, -math.log2(p) if p < 1 else 0.0)
    return worst


# ---------------------------------------------------------------------------
# 5. Entropy pool + cryptographic extractor
# ---------------------------------------------------------------------------

class EntropyPool:
    """Accumulates conditioned network entropy, extracts via HMAC-DRBG.

    The pool is *always* seeded from os.urandom() as well. Network noise is
    additional entropy, never the sole source. This is the difference between
    "novel entropy source" and "downgrade attack waiting to happen".
    """

    SEED_LEN = 32
    RESEED_INTERVAL = 1 << 16   # outputs before forced reseed

    def __init__(self, source: NoiseSource | None = None,
                 lsb_bits: int = 8, credit_per_bit: float = 0.25):
        self.src = source or NoiseSource()
        self.extractor = BitExtractor(lsb_bits=lsb_bits)
        self.health = HealthTests()
        self.credit_per_bit = credit_per_bit   # heavily discounted

        self._pool = hashlib.sha512()
        self._pool.update(os.urandom(64))      # never start empty
        self._credited_bits = 0.0
        self._recent: list[int] = []

        self._key = b"\x00" * self.SEED_LEN
        self._V = b"\x01" * self.SEED_LEN
        self._reseed_counter = self.RESEED_INTERVAL  # force reseed on first use
        self.stats: dict = {}

    # -- entropy input ------------------------------------------------------

    def harvest(self, samples: int = 32) -> float:
        """Sample the network, condition the bits, credit min-entropy.

        Returns the number of newly credited entropy bits.
        """
        gained = 0.0
        for _ in range(samples):
            rtt = self.src.sample()
            for bit in self.extractor.feed(rtt):
                healthy = self.health.update(bit)
                self._pool.update(bytes([bit]))
                self._recent.append(bit)
                if healthy:
                    gained += self.credit_per_bit

        # Also absorb the coarse jitter figure and a high-resolution timestamp.
        self._pool.update(struct.pack(" 4096:
            self._recent = self._recent[-4096:]

        # Cap the credit by the measured min-entropy, whichever is lower.
        if len(self._recent) >= 256:
            measured = min(
                min_entropy_per_bit(self._recent),
                markov_min_entropy(self._recent),
            )
            gained = min(gained, measured * len(self._recent))
            self.stats["min_entropy_per_bit"] = round(measured, 4)

        self._credited_bits += gained
        self.stats.update(
            credited_bits=round(self._credited_bits, 2),
            vn_efficiency=round(self.extractor.efficiency, 4),
            health_failures=self.health.failures,
            samples=self.src.samples_taken,
        )
        return gained

    # -- HMAC-DRBG (NIST SP 800-90A) ---------------------------------------

    def _hmac(self, key: bytes, data: bytes) -> bytes:
        return hmac.new(key, data, hashlib.sha256).digest()

    def _update(self, provided: bytes = b"") -> None:
        self._key = self._hmac(self._key, self._V + b"\x00" + provided)
        self._V = self._hmac(self._key, self._V)
        if provided:
            self._key = self._hmac(self._key, self._V + b"\x01" + provided)
            self._V = self._hmac(self._key, self._V)

    def reseed(self, min_entropy_bits: float = 128.0) -> None:
        """Reseed from (network pool || os.urandom).

        If the network hasn't supplied enough credited entropy, we harvest
        more — but we never block forever: os.urandom carries the floor.
        """
        attempts = 0
        while self._credited_bits < min_entropy_bits and attempts < 8:
            self.harvest(samples=24)
            attempts += 1

        network_seed = self._pool.digest()
        system_seed = os.urandom(48)
        self._update(network_seed + system_seed)

        # Re-key the pool without discarding its accumulated state.
        self._pool = hashlib.sha512(network_seed + os.urandom(32))
        self._credited_bits = 0.0
        self._reseed_counter = 0

    def _generate(self, nbytes: int) -> bytes:
        if self._reseed_counter >= self.RESEED_INTERVAL:
            self.reseed()

        out = b""
        while len(out) < nbytes:
            self._V = self._hmac(self._key, self._V)
            out += self._V
        self._update()
        self._reseed_counter += 1
        return out[:nbytes]


# ---------------------------------------------------------------------------
# 6. Public API
# ---------------------------------------------------------------------------

class AethelRandom:
    """Random number generator seeded by network noise.

    API mirrors the standard library where it makes sense.

    NOT a drop-in replacement for `secrets` in adversarial settings unless
    you have audited the entropy path. Use `secrets` when in doubt.
    """

    def __init__(self, pool: EntropyPool | None = None, warmup: int = 64):
        self.pool = pool or EntropyPool()
        if warmup:
            self.pool.harvest(samples=warmup)
        self.pool.reseed()

    # -- primitives --------------------------------------------------------

    def random_bytes(self, n: int) -> bytes:
        return self.pool._generate(n)

    def random_bits(self, k: int) -> int:
        if k <= 0:
            return 0
        nbytes = (k + 7) // 8
        val = int.from_bytes(self.random_bytes(nbytes), "big")
        return val >> (nbytes * 8 - k)

    def random(self) -> float:
        """Uniform float in [0, 1) with full 53-bit mantissa precision."""
        return self.random_bits(53) / (1 << 53)

    def randbelow(self, n: int) -> int:
        """Uniform int in [0, n) — rejection sampling, no modulo bias."""
        if n <= 0:
            raise ValueError("n must be positive")
        k = n.bit_length()
        while True:
            v = self.random_bits(k)
            if v < n:
                return v

    def randint(self, a: int, b: int) -> int:
        """Uniform int in [a, b], inclusive."""
        if b < a:
            raise ValueError("empty range")
        return a + self.randbelow(b - a + 1)

    def choice(self, seq):
        if not seq:
            raise IndexError("cannot choose from an empty sequence")
        return seq[self.randbelow(len(seq))]

    def shuffle(self, seq: list) -> None:
        """In-place Fisher-Yates."""
        for i in range(len(seq) - 1, 0, -1):
            j = self.randbelow(i + 1)
            seq[i], seq[j] = seq[j], seq[i]

    def sample(self, population, k: int) -> list:
        if k > len(population):
            raise ValueError("sample larger than population")
        pool = list(population)
        out = []
        for i in range(k):
            j = i + self.randbelow(len(pool) - i)
            pool[i], pool[j] = pool[j], pool[i]
            out.append(pool[i])
        return out

    # -- introspection -----------------------------------------------------

    def report(self) -> dict:
        """Honest status of the entropy path."""
        s = dict(self.pool.stats)
        s["jitter_ns"] = round(self.pool.src.jitter(), 1)
        s["source_healthy"] = self.pool.health.failures == 0
        s["mixed_with_os_urandom"] = True
        return s


# ---------------------------------------------------------------------------

if __name__ == "__main__":
    rng = AethelRandom(warmup=48)

    print("bytes  :", rng.random_bytes(16).hex())
    print("float  :", rng.random())
    print("d20    :", [rng.randint(1, 20) for _ in range(10)])

    deck = list(range(10))
    rng.shuffle(deck)
    print("shuffle:", deck)

    print("\nentropy path report:")
    for k, v in sorted(rng.report().items()):
        print(f"  {k:24s} {v}")


		 
Concept sketch · Aethel Noise Protocol

The idea in its bluntest form. Both peers observe the same shared statistical environment and derive role, timing and symbol from agreed patterns. Nothing is transmitted; both sides only listen and act. Published as a concept for critique.


ALGORITHM AethelNoiseProtocol(shared_seed, shared_observation_plan)

  loop forever:

      # 1. both peers observe the identical noise environment
      W  = observe_noise_window(shared_observation_plan)
      F  = extract_features(W)                 # entropy, jitter, drift, phase

      # 2. derive a common frame from the noise itself
      frame_id = hash(shared_seed, quantize(F.slow_components))

      # 3. role assignment from a deterministic function of the frame
      role = SENDER if parity(frame_id) == 0 else RECEIVER

      # 4. symbol channel: the sender does not transmit,
      #    it chooses WHICH pattern in the ambient noise to act upon
      if role == SENDER:
          target_pattern = select_pattern(message_bit, F)
          wait_until_pattern_occurs(target_pattern)   # no packet is emitted

      else:  # RECEIVER
          seen = wait_and_classify_pattern(F, timeout=frame_len)
          bit  = decode(seen)
          append_to_message(bit)

      advance_frame()

  # Open problems, stated honestly:
  #   - do two distant observers share enough correlated noise?
  #   - what is the achievable bitrate, if any? (bits per minute, at best)
  #   - is "acting on" a pattern distinguishable from not acting on it?
  # Phase 3 exists to answer these, and to publish a negative result if that is the answer.

		 

V. The Plan

Collect. Analyse. Find the connections. Build the grammar.

1. Collect at scale.
Deploy sensors across many geographic and topological vantage points. Continuous capture, multiple noise classes, precise timestamps, permanent archive.

2. Analyse with AI.
Train models over the corpus to find recurring structure that human eyes and classical statistics do not see. This is where the dataset stops being numbers and starts being a language.

3. Correlate with reality.
Join noise signatures to external ground truth: BGP events, outage reports, traffic cycles, weather, physical incidents. A pattern is only meaningful once it points at something.

4. Build patterns and forecasts.
From signatures to prediction: near-term network behaviour, anomaly precursors, congestion forecasting. The first genuinely useful output of the program.

5. Improve the instruments.
Higher resolution, lower overhead, better feature extraction, hardware-assisted timing, an open sensor spec others can deploy.

6. Attempt the Protocol.
Only after the corpus exists. Two peers, one algorithm, no packets. Success is a landmark; failure is a rigorous, publishable negative result.

VI. Cost

Modest, because this is instrumentation.

Working estimate for the first two phases. Aethel Noise is deliberately cheap compared to Aethel World — the expensive resource here is time on the wire, not tokens.

  • $95,000 — Distributed sensor fleet. ~40–60 nodes across regions: VPS, bare metal, edge devices, 12 months.
  • $60,000 — Storage and data pipeline. High-ingest time-series archive, backups, query layer.
  • $85,000 — Analysis compute (GPU). Model training and inference over the noise corpus.
  • $130,000 — Engineering & research staff. 2 people, 12 months: network engineer, ML researcher.
  • $18,000 — Precision timing hardware. GPS/PTP time sources, NICs with hardware timestamping.
  • $14,000 — Legal, compliance, ethics review. Passive-measurement compliance, responsible-disclosure policy.
  • $90,000 — Office, equipment, operations. Small physical base, workstations, security.
  • $18,000 — Contingency.
Total – phases 1–2: $420,000. Phase 3 (Protocol attempt): additional $180,000.

VII. How long

  • Setup: Month 1-2.
    Entity and contracts, node procurement, sensor hardening, calibration methodology.
  • Deployment and first corpus: Month 2-5.
    Fleet live across regions. Continuous capture begins. First public dataset release at month 5.
  • Analysis: Month 5-9.
    Model training, signature discovery, correlation with external ground truth, pattern catalogue v1.
  • Forecasting and instrument v2: Month 9-12.
    Predictive models, sensor redesign, open specification, technical report.
  • Protocol attempt: Months 12–18.
    Two-peer experiments across continents. Publication of the result — positive or negative..

Because data accumulates from month two, this program produces value continuously rather than at a single endpoint. It can be started small and expanded on evidence.

Funding a listening post is cheap. Not having the recording is what turns out to be expensive.