← Back to blog

Crossdating Your Logs: Tree-Ring Science for Aligning Clockless Event Streams

The fix isn't a better internal clock. It's an external anchor every observer can verify.

Published May 2026 · 9 min read

In October 2021, archaeologists settled an 800-year argument with three pieces of wood.

The wood came from L'Anse aux Meadows, a Norse settlement on the northern tip of Newfoundland — the only confirmed pre-Columbian European presence in the Americas. For decades the site was dated to "roughly 1000 CE," a fuzzy estimate with multi-decade uncertainty. Margot Kuitems, Michael Dee, and colleagues at the University of Groningen wanted a real year. They didn't build a better mass spectrometer; they didn't invent a new statistical method. They looked for a cosmic-ray spike.

In 2012, Fusa Miyake's lab at Nagoya University had reported a single-year anomaly in the carbon-14 record: a 1.2% surge in atmospheric ¹⁴C registered in Japanese cedar tree rings for the year 774–775 CE, roughly twenty times the normal year-to-year variation (Miyake et al., Nature 486:240–242). A second event followed in 993–994 CE. These "Miyake events" — caused by extreme solar particle storms — are imprinted simultaneously in every growing tree on Earth, an indelible cosmic timestamp.

Kuitems' team subsampled individual rings from three felled trees at L'Anse aux Meadows, all cut with metal tools, and ran accelerator mass spectrometry on each ring. All three samples showed the 993 spike. From the spike to the bark — the year the tree was felled — was exactly 28 rings in every case. The Norse cut those trees in 1021 CE, the same season, from three trees that had nothing in common but their shared sun (Nature 601:388–391).

The breakthrough wasn't precision. It was anchoring.

Your logs have the same problem those archaeologists had before 2012. And you can fix it the same way.

The floating chronology

Dendrochronology has a term for a tree-ring sequence whose internal order is reliable but whose absolute date is unknown: a floating chronology. You can count rings, measure widths, see that this year was wet and that one was a drought — but without an external anchor, the sequence drifts. It could be off by decades. It could be off by a millennium. The signal is internally consistent and externally untethered.

Andrew Ellicott Douglass, working at the University of Arizona in the early twentieth century, solved the regional version with crossdating. The insight is simple: trees in the same region share a climate. The same drought hits them all; the same wet spring widens every ring. Crossdating matches the pattern of wide and narrow rings across many samples until the relative sequences align. The technique requires no clock. It requires a shared signal and a reference chronology.

But regional crossdating won't anchor a Japanese cedar to a bristlecone pine. The climates are different; the patterns don't share enough structure. For global dating, you need a signal every tree on Earth records simultaneously. That is what Miyake events provide. A cosmic-ray spike doesn't care about local weather; it bathes the upper atmosphere, gets fixed into cellulose as ¹⁴C, and stays there. One ring, one year, every tree.

Now consider your distributed system.

The same problem, in software

Leslie Lamport's 1978 paper "Time, Clocks, and the Ordering of Events in a Distributed System" formalized the analogous problem for computers: across processes, "now" is ambiguous. Clocks drift, networks have variable latency, and there is no shared reference frame. Lamport's logical clocks track causality — if process A sent a message and B received it, A's send happened-before B's receive — without committing to wall-clock time. Vector clocks (Fidge 1988, Mattern 1989) extend this to detect concurrency across all processes.

Logical clocks are crossdating. They give you relative order from a shared signal — the message-passing graph — without needing a clock. They work brilliantly within a causal chain. They break at the boundaries, because two processes that never exchanged a message have no shared signal to align against. They cannot order events that share no history.

This is precisely the dendrochronological limit. Crossdating works within a climate region. Outside it, you need an external anchor.

The industry's answer at scale has been: throw money at better internal clocks. Google Spanner deploys GPS receivers and atomic clocks in every data center, builds them into TrueTime, and represents the current moment as an interval of uncertainty — typically 1 to 7 milliseconds wide. Before reporting a transaction committed, Spanner waits out the interval (the famous 7ms commit-wait), guaranteeing no later transaction can commit at an earlier timestamp. It works. It also requires Google-scale infrastructure.

CockroachDB takes the realistic path: NTP, with a configurable maximum clock offset (default 500 milliseconds). If a node detects its drift has exceeded roughly 80% of that bound versus its peers, it self-terminates rather than risk producing inconsistent timestamps. The database would rather die than lie about time. That is honest. It is also a confession: the internal clock cannot be trusted past a certain horizon, and the only safe move is to take the node out of service.

Both approaches are trying to make the floating chronology stop floating from the inside. Neither addresses the deeper problem, which is that no system is its own anchor.

What your traces actually look like

Distributed tracing tools — OpenTelemetry, now adopted in some form across the majority of production observability stacks — record spans across services and stitch them into a causal trace via propagated trace IDs. The trace ID is the shared signal. Stitching spans into traces is crossdating.

The error model is identical to dendrochronology's:

A missing ring is a year of such severe growth stress that the tree produces no visible band; if you count rings naively, you undercount. Some bristlecones in extreme alpine conditions miss dozens. In your traces, this is the dropped span — the service that processed the request but never emitted the event because the logger was rate-limited, or the agent crashed before flushing, or the network ate the export. Count spans naively and you undercount the work.

A false ring is an intra-annual density band — a midsummer dry spell that mimics an annual boundary. Count it as a ring and you overcount. Your false rings are retries that surface as separate events: the timeout-and-replay that logs two operations where one occurred, the at-least-once delivery that double-records the same payload, the leap-second hiccup that produces two "60th seconds" in a minute. Leap seconds are remarkably literal false rings — sixty seconds masquerading as sixty-one. Google's "leap smear," spreading the adjustment across a full day to avoid the discontinuity, is the operational fix.

A micro ring is so narrow it can be missed under the microscope. Yours are sub-resolution events — sub-millisecond spans collapsed by your sampling rate, the events your tracer dropped because the buffer was full, the things your APM agent didn't think were worth keeping.

Dendrochronology has a name for each of these. Most observability teams rediscover them, painfully, in incident retros.

The statistics map too. A 2023 paper in the ScienceDirect literature formalized the Type I / Type II error taxonomy for tree-ring matching: a Type I error is accepting a wrong alignment as correct; a Type II is missing a correct alignment. In trace assembly, Type I is the spurious causal link — two events you joined by trace ID that were actually unrelated retries. Type II is the missed chain — the slow-path failure where the originating span and the eventual error live on different traces because someone forgot to propagate context. The fields use different vocabulary for what is mathematically the same mistake.

Quorum, by another name

Tree-ring researchers report a number called Expressed Population Signal — EPS — that measures how well a finite sample of trees represents the underlying infinite-population chronology. The de facto threshold is EPS > 0.85. Trust the master chronology above 0.85; below it, the signal is too weak.

Allan Buras pointed out in 2017 that 0.85 was originally "arbitrarily chosen" and "not meant to be used" as a hard cutoff. It became one anyway. Whole regional chronologies live or die by it.

This should be familiar. Byzantine fault tolerance requires 2f + 1 nodes to tolerate f failures. Raft demands a majority quorum. EPS > 0.85 is the dendrochronological quorum threshold — an empirical line in the sand, defended more by convention than by derivation. Both fields are answering the same question — how many imperfect witnesses do you need before the consensus is trustworthy? — and both have settled on numbers that are part rigor, part folklore.

Buras' alternative, Subsample Signal Strength (SSS), tests whether subsets of the sample preserve the signal — does the chronology hold up when you partition the data? Distributed systems have a name for that test, too. It's partition tolerance.

Digital Miyake events

The local fixes — Spanner's commit-wait, CockroachDB's self-termination, EPS thresholds, vector clocks — all try to harden the internal record. None of them anchor it to anything outside the system.

A Miyake event is, by contrast, an external fingerprint with five remarkable properties:

  1. Global — recorded in every growing tree on Earth.
  2. Simultaneous — fixed within a single growing season.
  3. Unambiguous — twenty times above background, impossible to mistake.
  4. Indelible — locked into cellulose; cannot be retroactively edited.
  5. Independently verifiable — any lab can extract the spike from any sample.

What in your stack has these properties? Not NTP. NTP is an authority signal; trust the server or you have nothing. When NIST's Boulder, Colorado time service drifted by about four microseconds during a December 2025 power incident, the servers that depended on it kept answering queries — confidently — with a tainted reference. NTP fails as a master chronology for the same reason a single tree fails as one: a single witness has no error model for its own mistakes.

The candidate digital signals that score four-of-five or better are public blockchain finalizations. Ethereum's Beacon Chain partitions time into 12-second slots and finalizes blocks at epoch boundaries (32 slots, 6.4 minutes) once two-thirds of validator stake attests. A finalized block is globally observable to any node, precisely timed, immutable barring an economically prohibitive reorganization, and verifiable without trusting any single participant. Bitcoin block heights provide a coarser version with the same properties — every ten minutes, give or take.

Crossdating to a blockchain is what tools like OpenTimestamps and Chainpoint already do for documents: aggregate hashes into a Merkle tree, anchor the root in a Bitcoin transaction, and ship a self-contained proof that links the original data to the chain. The proof, in Chainpoint's own words, "can be verified without reliance on a trusted third party." That is the dendrochronological report — this ring is 28 rings after 993 — translated into bytes.

The vocabulary is different. The protocol is the one Kuitems' team used on those Norse junipers.

What to actually do

If you run an agent fleet, a distributed system, or anything that emits logs across machines you don't fully trust to keep time, the operational advice is two sentences long.

First: stop trying to fix the chronology from the inside. A better internal clock is a more expensive internal clock. If you don't have Spanner's budget and you do have a correctness requirement, an internal-only approach forces you toward either CockroachDB's self-terminate-on-drift posture (safe, but it takes nodes out) or the unsafe shrug of trusting NTP alone. The fix Kuitems et al. used wasn't a better instrument. It was the right external signal.

Second: anchor every session, by writ. Every agent, every process, every long-running job should record at least one universally verifiable timestamp — a finalized Ethereum block hash, a Bitcoin block height, an OpenTimestamps receipt — in its log on start, and again whenever it crosses a trust boundary. "I began this task after Ethereum block 22,456,789 was finalized" is a claim that holds up forever, against any party, without trusting your clock or theirs. "I began this task at 2026-05-28T11:44:00Z" is a promise your own clock could quietly break.

When you assemble a chronology after the fact — for an audit, for an incident review, for a regulatory dump — those anchors are your Miyake spikes. Find them in the log. Count the rings to the bark. You'll know exactly when the work happened, even if every clock in the system was lying.

Andrew Ellicott Douglass developed crossdating in the early twentieth century. Fusa Miyake found her spike in 2012. Margot Kuitems pinned the Vikings to 1021 CE in 2021. The 993 spike had been sitting in every tree alive that year for a thousand years before anyone thought to look for it.

The on-chain anchors are already there.

The question is whether your fleet is looking.

The Miyake spike for your agents has a name. It's called provenance.

The essay's prescription — anchor every session to a finalized public-blockchain timestamp at start and at each trust boundary — only works if your agent is actually recording the anchor in a place a future auditor can read. Chain of Consciousness anchors every agent action and observation to a verifiable external record, including the on-chain finalizations the essay recommends. It's the dendrochronological report your fleet writes as it works: each action carries the block hash it happened after, indelible and independently verifiable, so when you reconstruct the chronology you don't have to trust the clock that kept it.

pip install chain-of-consciousness · npm install chain-of-consciousness
Hosted Chain of Consciousness → · See a verified provenance chain