Back to Streaming Sketching

Differential Privacy under Continual Observation

September 28, 2026 Wasil Zafar 13 min read

Release streaming statistics with event-level privacy over time.

Table of Contents

  1. Publishing a Counter at Every Time Step
  2. Event-Level Privacy Across a Timeline
  3. Binary Trees, Noise and Composition
  4. Private Monitoring Under Continual Release
  5. Calibrate and Audit Continual-Observation Noise

Publishing a Counter at Every Time Step

A stream releases information repeatedly: after events $x_1,x_2,\ldots$, users see prefix totals $S_t=\sum_{i=1}^t x_i$ at every time $t$. Adding independent noise to every release seems natural, but one event influences every later prefix, so privacy loss can grow with the horizon.

Continual observation treats the entire output transcript as one mechanism. Two streams are event-level neighbors when they differ in one bounded event. An $(\varepsilon,\delta)$-private mechanism requires every set of possible transcripts to have nearly the same probability on neighboring streams. This protects one event’s participation, not necessarily all events belonging to one person.

For events $(1,0,1,1)$, exact prefixes are $(1,1,2,3)$. A binary-tree mechanism stores noisy sums for dyadic intervals such as $[1,4]$, $[1,2]$, $[3,4]$, and leaves. Prefix 3 is reconstructed from disjoint nodes $[1,2]$ and $[3,3]$. Reusing node noise across releases avoids paying separately for every prefix.

Sensitivity measures how much one event changes the released node vector. Composition combines privacy loss from multiple mechanisms. A privacy filter stops or modifies releases before a running budget is exceeded. Utility quantifies error, usually per time, uniformly over all times, or for a downstream decision. The horizon, event bound, neighboring relation, and release schedule are part of the theorem.

From event stream to private continual counterEvents enter a dyadic tree of noisy interval sums; each prefix combines a small number of nodes and is released as a private running total.Bounded eventsx₁, x₂, …, xₜone event differsuser-level not impliedNoisy dyadic treeevent touches ≤ L nodesreuse interval noiseprefix uses ≤ L nodesReleased timelineS̃₁, S̃₂, …jointly privatebudget monitored
ConceptIntuitionExact exampleNon-exampleAssumptionQuestion
Event-level neighborsChange one bounded eventReplace one $x_t\in[0,1]$Remove every event from one personContribution boundWhat is protected?
Continual observationPublish through timeRelease every prefix sumPublish only one final totalJoint transcript analyzedWhat does the observer see?
Dyadic nodeReusable interval totalNode $[1,4]$Arbitrary overlapping query setFixed horizon/treeWhat noise is reused?
SensitivityMaximum neighboring changeAt most $L$ node coordinates change by 1Average empirical changeWorst-case adjacencyHow much noise is required?
CompositionCombine privacy lossesAllocate budget across mechanismsTreat every release as freeMatching DP theoremHow does privacy accumulate?
Privacy filterStop before overspendingRefuse a new release at budget limitLog overspend afterwardSound accountantHow is adaptive use controlled?
UtilityAccuracy of released transcriptUniform maximum prefix errorPrivacy parameter aloneDeclared probability and metricIs the output useful?
Warm-up

Publishing a Counter at Every Time Step

For events $(1,0,1,1)$, compute exact prefixes $(1,1,2,3)$. Cover prefix 3 with dyadic intervals $[1,2]$ and $[3,3]$, and prefix 4 with $[1,4]$. If their fixed noises are $+0.4,-0.2,+0.1$, the released values are $\widetilde S_3=2+0.4-0.2=2.2$ and $\widetilde S_4=3+0.1=3.1$. Reflection: why must the same node keep the same noise whenever reused?

Event-Level Privacy Across a Timeline

Fix horizon $T$ and $L=\lceil\log_2T\rceil+1$. Build the complete dyadic tree. Release every node sum with independent Laplace noise of scale $L/\varepsilon$. Changing one event by at most one changes exactly one node on each level, so the vector of all node sums has global $\ell_1$ sensitivity at most $L$. The Laplace mechanism therefore makes the entire noisy node vector $\varepsilon$-differentially private; all prefix releases are post-processing.

Each prefix decomposes into at most $L$ disjoint dyadic nodes. Its noise is a sum of at most $L$ independent Laplace variables. The standard deviation is at most \[ \sqrt{2L}\frac{L}{\varepsilon}=O\!\left(\frac{L^{3/2}}{\varepsilon}\right). \] Tail bounds add dependence on failure probability $\beta$; a uniform-over-time guarantee also pays for controlling all $T$ releases, typically through a union bound or sharper maximal analysis. Constants depend on whether privacy is pure or approximate and whether error is pointwise, expected, or simultaneous.

This proof assumes a bounded event contribution, a fixed or safely extended horizon, independent correctly generated noise, no release of raw node sums, and no side channel leaking exact state. User-level privacy needs contribution limits across all of a person’s events and usually more noise.

Differential Privacy under Continual Observation: Event-Level Privacy Across a Timeline
flowchart TD
 A[Define neighboring event streams] --> B[Fix horizon T and depth L]
 B --> C[Map each event to one node per level]
 C --> D[Node vector sensitivity at most L]
 D --> E[Add calibrated Laplace or Gaussian noise]
 E --> F[Entire noisy node vector is private]
 F --> G[Decompose each prefix into at most L nodes]
 G --> H[Release prefix by post-processing]
 H --> I[Bound pointwise or uniform utility]
 A --> J{Unbounded contribution or wrong adjacency?}
 J -->|yes| K[Privacy claim invalid]
 J -->|no| B
 classDef navy fill:#17365D,color:#fff,stroke:#17365D;
 classDef teal fill:#147D84,color:#fff,stroke:#0D565B;
 classDef purple fill:#6C3BAA,color:#fff,stroke:#472675;
 classDef crimson fill:#C6284D,color:#fff,stroke:#861B35;
 class A,B,C,D navy;
 class E,F,G purple;
 class H,I teal;
 class J,K crimson;
Dyadic tree for eight eventsOne event contributes to a leaf, a pair interval, a four-event interval, and the root. Prefix six is reconstructed from intervals one through four and five through six.[1,8] + noise[1,4][5,8][1,2][3,4][5,6][7,8]Prefix 6 = noisy [1,4] + noisy [5,6]used for prefix 6other released node

For a horizon-$T$ bounded-event counter, the noisy dyadic-node vector has sensitivity at most $L=\lceil\log_2T\rceil+1$. Laplace scale $L/\varepsilon$ gives pure event-level $\varepsilon$-DP, and each prefix sums at most $L$ noises, producing the characteristic logarithmic utility cost. Different adjacency, approximate DP, or uniform-error goals require recalibration.

Binary Trees, Noise and Composition

The tree can be viewed as one vector-valued Laplace mechanism or as composition across levels. The vector view makes reuse safe: publish each noisy node once, and derive every prefix by post-processing. Resampling noise whenever a node is reused creates additional releases and invalidates the original accounting.

Worked Example

Binary Trees, Noise and Composition

For $T=8$, an event at time 6 belongs to intervals $[6,6]$, $[5,6]$, $[5,8]$, and $[1,8]$, so it affects $L=4$ released node sums. Prefix 6 decomposes as $[1,4]\cup[5,6]$ and uses two noises. With $\varepsilon=1$, the conservative pure-DP scale is 4. Perturb the design by independently re-noising $[1,4]$ every time it appears: repeated observations average away noise and incur unaccounted composition.

import math
import random

def laplace(rng, scale):
    u = rng.random() - 0.5
    return -scale * math.copysign(math.log1p(-2 * abs(u)), u)

def dyadic_intervals(horizon):
    if horizon < 1 or horizon & (horizon - 1):
        raise ValueError("horizon must be a positive power of two")
    intervals = []
    size = 1
    while size <= horizon:
        for start in range(0, horizon, size):
            intervals.append((start, start + size))
        size *= 2
    return intervals

def private_prefixes(events, epsilon, seed=7):
    if epsilon <= 0 or not events:
        raise ValueError("positive epsilon and nonempty events required")
    if any(value < 0 or value > 1 for value in events):
        raise ValueError("events must lie in [0,1]")
    horizon = len(events)
    intervals = dyadic_intervals(horizon)
    levels = int(math.log2(horizon)) + 1
    rng = random.Random(seed)
    noisy = {}
    for start, end in intervals:
        noisy[(start, end)] = sum(events[start:end]) + laplace(rng, levels / epsilon)

    output = []
    for end in range(1, horizon + 1):
        cursor, total = end, 0.0
        while cursor:
            width = cursor & -cursor
            total += noisy[(cursor - width, cursor)]
            cursor -= width
        output.append(total)
    return output, noisy

events = [1, 0, 1, 1, 0, 1, 0, 1]
released, nodes = private_prefixes(events, epsilon=1.0, seed=11)
released_again, nodes_again = private_prefixes(events, epsilon=1.0, seed=11)
assert released == released_again and nodes == nodes_again
assert len(released) == len(events)
assert len(nodes) == 15

changed = events[:]
changed[5] = 0
intervals = dyadic_intervals(8)
touched = [interval_ for interval_ in intervals
           if interval_[0] <= 5 < interval_[1]]
assert len(touched) == 4

try:
    private_prefixes([0, 2, 1, 0], 1.0)
    raise AssertionError("unbounded event accepted")
except ValueError:
    pass

print("exact prefixes:", [sum(events[:i]) for i in range(1, 9)])
print("private prefixes:", [round(x, 3) for x in released])
print("nodes touched by event 6:", touched)
MechanismPrivacy modelError shapeStrengthFailure modePrefer when
Independent noise per prefixCompose $T$ releasesNoise grows badly under fixed total budgetSimpleIgnores repeated influenceTiny horizons
Binary tree + LaplacePure event-level DPPolylogarithmic horizon dependenceAuditable reuseWrong horizon or resamplingRunning counts
Binary tree + GaussianApproximate DP with accountantDepends on composition/accountingBetter regimes possibleIncorrect $delta$ allocationLarger systems
User-level treeBound all contributions per userLarger sensitivity/noiseStronger semantic protectionUnbounded user activityPerson-level guarantees
Privacy filter/odometerAdaptive release sequenceBudget-dependentStops overspendUnsound accountantInteractive monitoring
Batch final releaseOne terminal statisticMuch lower noiseBest utilityNo continual visibilityEnd-of-period reports

Private Monitoring Under Continual Release

Teaching should compare exact prefixes, independently noised prefixes, and a tree transcript on the same eight events. In professional monitoring, releases might track incident counts, service demand, or public-health events; the data contract must bound contributions before noise calibration. A failure case is a dashboard claiming user-level protection while one person can generate thousands of events.

Production design must specify horizon extension, late events, corrections, retention, random-number generation, privacy ledger, access control, and downstream post-processing. A privacy filter can reject optional releases when the accountant reaches its limit. Noise is not a substitute for authentication or secure raw-state handling.

Invalidating evidence includes unbounded contributions, exact node sums in logs, resampled node noise, budget resets without population separation, empirical noise variance inconsistent with calibration, missing late-event policy, $delta$ larger than an acceptable failure probability, or decisions whose tolerance is smaller than the measured error.

Private continual monitoring serviceBounded events enter a protected tree mechanism, a privacy accountant controls release, and published monitoring outputs are audited for privacy and utility.Contribution gateidentity + boundsvalidated eventslate / excess eventsPrivate tree serviceprotected node statenoise + accountantfilter / releaseno raw logsMonitoring outputprivate prefixesutility auditdecision threshold
Differential Privacy under Continual Observation: Private Monitoring Under Continual Release
flowchart TD
 A[Define event or user adjacency] --> B[Bound and validate contributions]
 B --> C[Choose horizon release cadence epsilon delta]
 C --> D[Select tree mechanism and accountant]
 D --> E[Simulate privacy and utility]
 E --> F{Risk and error acceptable?}
 F -->|yes| G[Deploy protected state and ledger]
 F -->|no| H[Reduce releases increase aggregation or reject]
 G --> I[Monitor budget noise and downstream harm]
 I --> J{Assumptions unchanged?}
 J -->|yes| I
 J -->|no| H
 classDef navy fill:#17365D,color:#fff,stroke:#17365D;
 classDef teal fill:#147D84,color:#fff,stroke:#0D565B;
 classDef purple fill:#6C3BAA,color:#fff,stroke:#472675;
 classDef crimson fill:#C6284D,color:#fff,stroke:#861B35;
 class A,B,C navy;
 class D,E,G purple;
 class F,I teal;
 class H,J crimson;
Case Study

Private Monitoring Under Continual Release: Differential Privacy under Continual Observation

For hourly incident counts over $T=8192$ hours: choose event-level or user-level adjacency; cap contributions before ingestion; compute tree depth and noise from the selected pure or approximate-DP accountant; simulate pointwise and maximum error on realistic seasonality; test late data, horizon rollover, repeated dashboard reads, RNG failure, raw-log leakage, and budget exhaustion; define a privacy filter for optional breakdowns; and compare alert precision against exact internal audits. Deploy only when the semantic guarantee matches stakeholders and operational decisions tolerate the error.

Calibrate and Audit Continual-Observation Noise

An audit must reproduce both sensitivity and transcript reconstruction. Sampling noise alone does not prove privacy, while a symbolic privacy proof alone does not establish operational utility.

Practice

Practice: Calibrate and Audit Continual-Observation Noise

Use $T=16$, events in $[0,1]$, $ arepsilon=1$, and pure event-level DP. Draw the dyadic tree; list the five nodes touched by event 11; prove node-vector $ell_1$ sensitivity is at most 5; decompose prefixes 7, 11, and 16; calibrate Laplace scale 5; compute variance for each prefix; simulate 10,000 transcripts and report pointwise plus maximum error; test a neighboring stream; then break the mechanism by resampling reused nodes, logging exact sums, allowing value 4, and resetting the budget. Extend to user-level bounds and an $( arepsilon,delta)$ accountant. Rubric: 25% adjacency/sensitivity, 20% tree trace, 20% privacy proof, 15% utility experiment, 10% failure diagnosis, 10% production policy.

Checklist: define event- or user-level neighbors; bound contributions before release; fix or safely extend the horizon; count every tree level; calibrate noise to the matching sensitivity and accountant; reuse each noisy node; treat the full transcript as output; budget optional releases with a sound filter; protect raw state and randomness; and evaluate pointwise, uniform, and decision-level utility. The next bridge is to private histograms, adaptive privacy accounting, pan-privacy, and robust private streaming.