cd /news/ai-agents/show-hn-morletdb-is-a-postgres-exten… · home › topics › ai-agents › article
[ARTICLE · art-169721] src=github.com ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

Show HN: MorletDB is a Postgres extension for waveforms

MorletDB, a PostgreSQL extension released by Merkhet, stores waveform recordings as intact channels with sample rate, unit and calibration metadata rather than scattering samples across rows, and answers similarity queries over a bounded range of time shifts. The extension ships a Grizzbit codec that codes raw samples bit-exactly and is often several times smaller than general-purpose time-series compression on the same data, though it is weaker on floats without a grid. MorletDB exposes plain SQL functions such as morlet_search_envelope_probes and morlet_read_interval so AI agents can retrieve matching intervals and original samples, and a demo loading four days of public seismic recordings through a read-only MCP server found that four of the five closest matches were other catalogued earthquakes.

read12 min views2 publishedNov 14, 2026
Show HN: MorletDB is a Postgres extension for waveforms
Image: Michielbdejong (auto-discovered)

ἁρμονίη ἀφανὴς φανερῆς κρείττων.

<sub>— Heraclitus, fragment B54</sub>

MorletDB is a PostgreSQL extension for waveform archives.

Select an interval and ask “Have we seen a signal like this before?” — every answer is a recording

and a sample interval you can read back, for an engineer or for an AI agent.

AI agents · Install · Use · Research · Explore · Evidence · Website

Recordings, not rows. A recording is one channel with a sample rate, a unit, a calibration and its acquisition metadata. MorletDB keeps that unit intact inside PostgreSQL, instead of scattering samples over rows or vectors.

  • Lossless and compact. Raw samples are coded bit-exactly, often several times smaller than general-purpose time-series compression on the same data. The codec,Grizzbit , stores each chunk on the value grid it detects, with no precision parameter, and is weaker on floats without a grid.
  • Search that tolerates time shifts. Two occurrences of the same pattern rarely line up with a window grid. MorletDB scores similarity over a bounded range of shifts, exactly or through a standard vector index.
  • Every hit traces back. Results are intervals of recordings, with their metadata, that read back the original samples.
  • Safe to keep ingesting. Appends, search representations and index rows commit in one transaction; retries are verified, gaps and overlaps are rejected.
  • Plain PostgreSQL. SQL functions, your roles and row-level security, your backups. pgvector is optional.

Give your agents signal evidence to reason with. An investigation agent can search your recording archive before it answers: have we seen a signal like this before? MorletDB supplies the matching intervals and their original samples; your application connects them to analysis tools, maintenance notes or experiment records.

The tool is plain SQL, so it runs with the agent's database role, row-level security and limits:

-- a bounded tool an agent can call: similar intervals for a source interval and an asset
CREATE FUNCTION agent_similar_intervals(recording text, from_sample bigint, to_sample bigint,
                                        asset text, k integer DEFAULT 5)
RETURNS TABLE (recording_id text, start_sample bigint, end_sample bigint, distance float8)
LANGUAGE sql STABLE AS $$
  SELECT recording_id, start_sample, end_sample, distance
  FROM morlet_search_envelope_probes('signals', recording, from_sample, to_sample,
                                     jsonb_build_object('asset', asset), least(k, 20))
$$;
-- and to bring evidence into analysis: morlet_read_interval('signals', recording_id, start, end)
Use case What the agent does
Anomaly investigation an external detector flags an interval; the agent checks whether it resembles earlier events and under which operating conditions
Autonomous factory workflows gathers signal evidence across machine runs before proposing maintenance, a quality check or an escalation; your application decides which actions need approval
Engineering investigation retrieves earlier test runs to explain a changed transient, with the experiment metadata
Research and labelling assistants collects similar intervals for human review, annotation or model evaluation

Try it with Claude: the agent demo loads four days of public seismic recordings with Docker and lets Claude Code or Claude Desktop investigate an earthquake through the read-only MCP server. Four of the five closest matches it finds are other catalogued earthquakes. The machine demo does the same for a bearing run to failure (NASA/IMS): Claude finds when the failing bearing first changed and whether the others show it.

Important

The SQL function above is an integration pattern; the bundled MCP server only reads. Detection models, agent orchestration and machine controls come from your application. On seismic data, this search returns catalogued earthquakes about twice as often as zero-lag similarity (E5, E5-H); anomaly workflows have not been evaluated, and similarity alone does not establish normality, a fault or its cause.

  1. Append chunks of a recording at their global sample offset.
  2. Build search representations, such as amplitude envelopes of 4096-sample windows, in the same transaction, so new windows are searchable at commit.
  3. Search with a query interval: exact scoring of every eligible window, or approximate candidates from a vector index followed by exact re-ranking.
  4. Read back each match as the original samples, with its recording metadata.

Grizzbit is MorletDB's lossless chunk codec: what goes in comes back bit for bit. Each chunk of up to 32,768 float64 samples is stored in the smallest of three exact forms: integer steps on the value grid the encoder detects (predicted and Rice-coded as in FLAC, with last-place corrections where needed), the float64 bit patterns coded the same way, or plain float64. Every chunk carries a CRC-32C checksum.

  • No precision parameter. Nothing can be configured to drop detail. Finite values up to ±10⁹ are stored exactly; anything else is refused at write time, never altered.
  • Checked when written. Withmorlet.verify_appends on (the default), every append decodes each chunk again and compares it bit for bit before storing it.
  • Strong on gridded signals, weaker without a grid. On ADC counts, fixed decimals and calibrated waveforms it is among the smallest codecs measured (B4); on floats without a clean grid it is 1.6–4× larger than the best. Grizzbit 2, an attempt to close that gap, failed study B5 and was not shipped (#326).
  • Float32 codings, on by default (chunk format version 6): samples that are float32 values, such as PLC and CNC controller signals, are coded in float32 terms where that is smaller. It passed study B8: on IU4 force and controller channels 3.02 and 2.29 bytes per sample instead of 6.60 and 6.52, never a larger chunk (results ;SET morlet.float32_chunks = off turns it off).
  • Grizzbit 2.1, opt-in (morlet.chunk_format = grizzbit21 ): a per-chunk linear predictor and corrections per grid index, kept only where smaller. It passed study B6: waveforms 12.6% smaller in total, never larger (how to use it ).
  • Nothing here is novel , and the docs say so: Grizzbit is a named, specified format built from established lossless techniques (FLAC's fixed predictors and Rice coding, SEED and EDF style gain and offset).

More: docs/grizzbit.md · codecs at a glance · format specification · crates/grizzbit

Full setup and query examples: installation and query guide. Source builds use Rust 1.99.0 and pgrx 0.19.3.

Run the release image (prebuilt: PostgreSQL 17 with pg_morlet, Linux amd64 and arm64, with SBOM and build provenance; no pgvector, so no probe search; choose your own password):

docker run -d --name morletdb -e POSTGRES_USER=morlet -e POSTGRES_PASSWORD=change-me \
  -e POSTGRES_DB=morlet -p 127.0.0.1:54329:5432 -v morletdb-data:/var/lib/postgresql/data \
  ghcr.io/merkhet-io/morletdb:0.3.14-alpha.1
docker exec morletdb psql -U morlet -d morlet -c "CREATE EXTENSION pg_morlet"

Try the walkthrough (builds a PostgreSQL 17 preview for your machine, amd64 or arm64; imports synthetic data, finds a known interval and reconstructs its matches):

docker compose run --rm walkthrough

Build from source with Rust and pgrx (PostgreSQL 17, 18 or 19; on Debian/Ubuntu install build-essential bison flex libreadline-dev zlib1g-dev libclang-dev clang pkg-config libssl-dev first):

cargo install cargo-pgrx --version 0.19.3 --locked
cargo pgrx init --pg17 download          # or --pg18, --pg19 /path/to/pg_config, or --pg17 /path/to/pg_config
cd extensions/pg_morlet
cargo pgrx install --release --pg-config "$(sed -n 's/^pg17 = "\(.*\)"/\1/p' ~/.pgrx/config.toml)"

Then, in your database:

CREATE EXTENSION pg_morlet;
CREATE EXTENSION vector;   -- optional: only for probe search (pgvector 0.8 or later)

Use it from Python: ingest a NumPy waveform, read exact intervals and search similar ones with the Python SDK (sdk/python, alpha), starting with a runnable quickstart.

Let an AI agent investigate your recordings through a read-only MCP server (Claude Code, Claude Desktop and other MCP clients): MorletDB for AI agents.

What MorletDB works with (PostgreSQL, pgvector, Docker, MCP, Grafana, Python and Jupyter, file formats), with how each is verified: integrations.

Import your own files (WAV, EDF, miniSEED, CSV) and get a first search query: from files to a first search.

Supported versions, CI coverage and local test commands: PostgreSQL compatibility. Upgrading an existing installation: existing installations.

-- a collection holds recordings, raw chunks and search representations
SELECT morlet_create_stream_collection('public', 'signals');
SELECT morlet_enable_envelopes('signals');

-- one recording = one channel: id, sample rate, channel, unit, metadata
SELECT morlet_start_recording('signals', 'pump-7', 25600, 'accel_x', 'g', '{"asset": "P7"}');

-- append samples at their global offset; repeating an identical append is a no-op
SELECT morlet_append_chunk('signals', 'pump-7', 0,
  ARRAY(SELECT sin(i / 40.0) * (1 + (i / 3000) % 3) FROM generate_series(0, 32767) i));

-- build envelopes (4096-sample windows, stride 2048); call it in the append
-- transaction to make new windows searchable at commit
SELECT morlet_build_envelopes('signals', 'pump-7');

-- where else does samples 8192..12288 occur? shift-tolerant search
SELECT recording_id, start_sample, end_sample, distance
FROM morlet_search_similar('signals', 'pump-7', 8192, 12288, '{}', 5);

-- read any match back as original samples
SELECT samples FROM morlet_read_interval('signals', 'pump-7', 26624, 30720);

-- progress and backlog per window size
SELECT * FROM morlet_collection_status('signals');

For large archives, enable probe search (needs pgvector): it finds candidates through an HNSW index and re-ranks them with the same exact score. It is approximate in which windows it considers, never in the distances it returns.

SELECT morlet_enable_envelope_probes('signals', 4096);
SELECT * FROM morlet_search_similar('signals', 'pump-7', 8192, 12288);   -- now uses probes

morlet_search_similar uses probe search when it is enabled for the window size and exact search otherwise; morlet_search_envelope_interval and morlet_search_envelope_probes choose a path explicitly.

The fifth argument of the interval searches is a metadata filter ('{"asset": "P7"}'). Small filtered galleries are routed to exact search automatically. Searches run with the caller's permissions, including row-level security.

More: SQL reference · search contract · recording workflow · operations · access control · Parquet import

Task Main functions
Create a collection and recording morlet_create_stream_collection ,morlet_start_recording
Append and inspect progress morlet_append_chunk ,morlet_stream_status ,morlet_collection_status
Read original samples morlet_read_interval
Search Morlet/statistical features morlet_search_streams ,morlet_search_interval
Materialize envelopes morlet_enable_envelopes ,morlet_build_envelopes
Search similar intervals (probes if enabled, else exact) morlet_search_similar
Search bounded-lag envelopes exactly morlet_search_envelopes ,morlet_search_envelope_interval
Enable and query optional indexed probes morlet_enable_envelope_probes ,morlet_search_envelope_probes
Inspect storage morlet_recording_storage ,morlet_collection_storage

The SQL guide walks through storing two synthetic recordings, building envelopes, filtering a query, and reading its matches. Intervals are zero-based and half-open; feature and envelope search use different scorers. Envelopes require an explicit builder; raw ingestion alone does not make a recording searchable through that path. Probe candidate selection is approximate above the exact-routing threshold. See the search contract for budgets, permissions, source exclusion, overlap suppression and snapshot semantics.

| MorletDB: Recordings, Not Rows — Lag-Tolerant Waveform Retrieval in PostgreSQL Version 0.25 Bounded-lag cross-correlation is the right score when matches may be shifted in time, but standard vector indexes rank by an unshifted metric. Because each lag term is an inner product with a shifted copy of the query, a sparse grid of shifted probes into an ordinary pgvector HNSW index, re-ranked with the exact score, recovers the shift-tolerant top ten. | On held-out seismic data (234,622 windows): |

What it costs and where it is not shown: a probe table and index of about the size of the raw data, slower search under filters that exclude a query's neighbours, and extra write-ahead logging for index maintenance. The paper's retrieval results are seismic. Study N1 added appliance identification from current transients (PLAID): exact envelope search named the appliance about as well as a current-feature 1-NN (H1 passed), but its distances did not flag unknown appliances (H2 failed). Other signals, diagnostic relevance and long-running operation are not shown. Supplement · study overview · reproduce the numbers · main results

| Recording viewer. A local, read-only page for a recording, its context lanes (program, block, tool, part), segments and labels; compare a segment with the same context on other parts, or find similar windows.tools/viewer

python -m pip install -r tools/viewer/requirements.txt
DATABASE_URL=postgresql://... python tools/viewer/demo.py
DATABASE_URL=postgresql://... python tools/viewer/viewer.py

| | | Sleep staging by example. 153 Sleep-EDF nights stored as raw recordings; click an epoch of a held-out night and see the 40 most similar expert-scored epochs from other people, each read back from its interval. A research demo, not a medical device.examples/sleep | |

Also: compare a machine with its own history under the same operating context · visual notebook · Parquet importer

Every evaluation fixes its protocol before reading the data, runs once, and reports the result, including negative ones. In short:

  • Held up: lossless storage (1.2–6× smaller than TimescaleDB compression on the same samples), exact and shift-tolerant search, probe search on standard indexes, retrieval of labelled examples as explanation, comparison under the same operating context, access control.
  • Did not hold up: Morlet features have not beaten simple FFT band powers on bearings, ECG or sleep EEG; no wavelet or filter type has beaten a plain band-pass filter for bearing faults; own-history comparison has not warned earlier than a plain RMS alarm on run-to-failure bearings.

Every evaluation with its data, baselines, confidence intervals and verdict: docs/evaluations.md.

Append-only recordings; 1–32,768 samples per chunk; explicit channel and unit; no resampling, gap interpolation, background extraction worker or cross-channel search. Exact search scans eligible windows; probe search is approximate and opt-in. Alpha: no stable API or schema guarantee yet. Details: support boundaries · compatibility · numerical contract.

The name. MorletDB honours Jean Morlet and the project's starting hypothesis that Morlet wavelets would suit search in technical signal archives. The system is not tied to that transform, and our evaluations have not shown a general advantage for it.

Path What
extensions/pg_morlet the PostgreSQL extension: storage, ingestion, interval reads, search
crates/grizzbit Grizzbit , the lossless chunk codec (specification )
crates/morlet-core portable Rust core: envelopes, features, checkpoints; re-exports the codec
tools Parquet import, Python client, recording viewer, independent numerical reference
examples sleep staging, notebooks and other worked examples
evaluation ·benchmarks frozen evaluations and workload benchmarks
research/paper the paper, supplement and reproduction package
make test       # Rust core and formatting
make pg-test    # needs a pgrx-managed PostgreSQL

Contributing · Development · Support · Security · Roadmap · Brand assets

Copyright 2026 MorletDB contributors. Code, documentation and original brand artwork are licensed under the Apache License, Version 2.0. Every part of MorletDB, integrations included, is open source under that licence; there is no enterprise edition (licensing). The technical report's text and figures are licensed under CC BY 4.0. A Merkhet project.

── more in #ai-agents 4 stories · sorted by recency
── more on @morletdb 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/show-hn-morletdb-is-…] indexed:0 read:12min 2026-11-14 · —