Architecture
How Astervis is assembled on your server.
Astervis is a small stack of Docker containers that runs on the same server as your PBX. It taps into the call records Asterisk already writes, copies them into its own analytics database, and serves a dashboard — without ever touching the cloud. This page is the mental model: what the pieces are, how data moves between them, and where the AI fits in.
Everything here lives on your server. Call records and recordings are replicated into a local database and never leave the machine — the one exception is optional AI analysis, which sends call audio to Google Gemini. See AI & privacy.
The data flow
A call finishes, Asterisk writes a CDR row into its MySQL/MariaDB database, and from
there Astervis takes over. The CDC service notices the new row in the binary log,
hands it to the replication consumer, and it lands in TimescaleDB. The backend and
admin read from TimescaleDB and render the dashboard you open on port 8443.
If the call has a recording and AI is enabled, a second path kicks in: the queue picks up the audio, sends it to the AI gateway (Gemini on Vertex AI), and the resulting transcript and analysis are written back into the same database — so they show up on the call card alongside the metrics.
┌─────────────────────┐
│ Asterisk / FreePBX │ call ends → CDR row + (optional) recording
│ MySQL CDR (host) │
└──────────┬──────────┘
│ binary log
▼
┌─────────────────────┐
│ litcdc (CDC) │ reads binlog, snapshots then streams
└──────────┬──────────┘
│ HTTP long-poll feed
▼
┌─────────────────────┐
│ replication │ consumes feed → writes rows
└──────────┬──────────┘
▼
┌─────────────────────┐ ┌──────────────────────────────┐
│ TimescaleDB │◀──────▶│ backend (app) + admin (web) │
│ (Postgres + TS + │ └───────────────┬──────────────┘
│ pgvector) │ │
└──────────┬──────────┘ ▼
│ ┌──────────────────┐
│ recordings │ nginx :8443 │ ← you, the dashboard
▼ └──────────────────┘
┌─────────────────────┐ audio ┌──────────────────────────┐
│ queue (BullMQ) │───────────▶│ AI gateway (Gemini) │
└─────────────────────┘ └────────────┬─────────────┘
▲ │ transcript + analysis
└─────────────────────────────────────┘ (back into TimescaleDB)What gets installed
The installer renders a docker-compose.yml into /opt/astervis and brings up the
following services. Only nginx is reachable from outside the machine; everything
else binds to 127.0.0.1.
| Service | Container | What it does | Host port |
|---|---|---|---|
timescaledb | astervis-timescaledb | Postgres 16 + TimescaleDB + pgvector — the analytics store | 127.0.0.1:5432 |
redis | astervis-redis | Session cache + BullMQ queue backend | 127.0.0.1:6380 |
migrate | astervis-migrate | One-shot: applies DB migrations, then exits | — |
app | astervis-app | Backend API (Elysia.js on Bun) | 127.0.0.1:5555 |
nextjs | astervis-nextjs | Admin dashboard (Next.js / React) | 127.0.0.1:6464 |
queue | astervis-queue | BullMQ worker — AI jobs, syncs, insights | — |
litcdc | astervis-litcdc | The CDC service — reads the Asterisk binlog | 127.0.0.1:18088 |
replication | astervis-replication | Consumes the litcdc feed → TimescaleDB | — |
nginx | astervis-nginx | Reverse proxy + TLS termination | 8443 |
On disk it looks like this:
There is no Redpanda, Kafka, or Debezium in this list. Older versions of the
stack used a Debezium connector plus a Redpanda broker for change capture — both were
removed when litcdc landed (installer v0.0.82+). If you see a data/redpanda/
directory, it's a leftover from an old install and can be deleted.
litcdc — the CDC pipeline
CDC (change data capture) is how Astervis keeps its analytics database in lockstep with Asterisk without polling MySQL and without slowing the PBX down. The whole job is done by litcdc, a single Rust binary (~10–25 MiB resident) that reads the MySQL/MariaDB binary log directly.
It runs in two phases:
Consistent initial snapshot
On first run, litcdc takes a consistent snapshot of the existing cdr table — your
full historical call backfill — using a brief FLUSH TABLES WITH READ LOCK to get a
clean cut. This is why the CDC database user needs the LOCK TABLES grant.
Realtime streaming
Once the snapshot is done, litcdc switches to tailing the binlog and streams every new and changed row as it happens. Events are buffered in a durable, bounded local log so nothing is lost across a restart — and because the buffer survives reboots, litcdc never re-snapshots.
The replication consumer pulls those events over an HTTP long-poll feed (litcdc
listens on litcdc:8088 inside the Docker network, published to 127.0.0.1:18088 on
the host) and writes them into TimescaleDB. The buffer is capped at 64 MiB; once events
are consumed past that cap they're truncated, and the writer pauses if nothing is
draining — so a stalled consumer can never blow up disk usage.
litcdc replaced the old Debezium + Redpanda pipeline. That pair needed a JVM and a full message broker and cost roughly 640 MiB of RAM; litcdc does the same job — durable buffering, offsets, replay, backpressure — in around 30 MiB, with no broker and no JVM. That single change is why the whole stack now fits in ~0.6 GB and the RAM minimum dropped to 2 GB.
litcdc speaks a Debezium-compatible {schema, payload} envelope on its feed, so the
replication consumer needed no rewrite when the broker was removed. The compatibility
is purely on the wire — there is no Debezium or Kafka anywhere in the stack.
The AI pipeline (optional)
Analytics work fully on their own — the AI pipeline is a separate, optional layer that turns recordings into transcripts, QA scores, and searchable text. It only runs if you hold an AI license tier, and it's the only part of Astervis that sends data off the server.
When a CDR with a recording lands, the queue worker submits the audio to the AI
gateway, which runs a single multimodal Gemini call on Google Vertex AI —
transcription, speaker diarization, sentiment, category, and (if configured) rubric-based
QA all in one request. Results come back via webhook into the ai_transcripts table,
moving through pending → processing → completed. From there they're embedded with
gemini-embedding-001 into pgvector for semantic search.
AI call analytics
What the model returns for every recorded call.
AI & privacy
Exactly what leaves your server, and what stays.
Nothing about AI is required. If your calls can't leave the building, simply don't enable it — the dashboards, CDR replication, and integrations all work without it.
Resource footprint
The full stack sits at roughly 0.6 GB of RAM at rest — most of the savings come
from litcdc replacing the old broker-based CDC. The hard minimum to install is 2 GB of
RAM, 1 CPU core, and 15 GB of free disk (checked on the Docker data partition, not /).
4 GB and 2 cores are recommended for comfortable headroom during the initial snapshot.
Network surface
This is the part worth internalizing for anyone hardening the box: Astervis exposes a single port.
| Port | Service | Notes |
|---|---|---|
8443 | nginx | HTTPS dashboard — https://<domain-or-IP>:8443 |
nginx terminates TLS and proxies to the backend and admin internally. This is the only port you open in a firewall.
Your data stays on your server. CDRs, recordings, transcripts, and the analytics
database all live inside /opt/astervis on your machine. The product never runs in
the cloud. The only outbound traffic is license verification and — if you turn it on —
AI analysis to Google Gemini.
Where to go next
Last updated on