Skip to content

Releases: AndrewCTF/velocity

v1.0.1 — bound the archive's write-ahead log

Choose a tag to compare

@AndrewCTF AndrewCTF released this 16 Jul 13:06
175297b

Patch release. If you are running v1.0.0, upgrade — it ships an archive that
grows without bound.

The fix

v1.0.0's replay bar asked the backend for archive coverage every 5 seconds, and
each request scanned the whole archive. That scan takes longer than the gap
between polls, so a read transaction was always open — and SQLite can only
checkpoint its write-ahead log past the oldest live reader. The recorder never
stops writing, so the log grew forever.

It compounds, which is why it runs away rather than settling: every page read has
to search the log for that page's newest version, so a bigger log makes the scan
slower, which holds the reader open longer, which grows the log faster.

On the development box that produced a 49.6 GB write-ahead log against 15.2 GB
of actual data
— 71 GB of disk for 15 GB of archive. A checkpoint reclaimed
48.6 GB of it, so 98% was redundant page versions that could never be written
back. Left recording on a smaller disk, it fills it.

Coverage is now cached behind a lock, so the scan runs at most once every two
minutes and never twice at once, and the log is bounded by journal_size_limit
so it is truncated back whatever the read pattern does.

Replaying the exact workload that caused it, against a real 16 GB archive:

v1.0.0 v1.0.1
write-ahead log grew to 49.6 GB 0 MB, flat
memory +4-5 GB/min, peaked at 83 GB flat, ~660 MB
coverage request 73 s, every call 14.8 s once, then cached

Upgrading

Nothing to migrate. Pull and restart; the oversized log from a v1.0.0 run is
checkpointed back into the database on the next clean start. If you want the
disk back immediately, stop the API and run:

sqlite3 data/history.db "PRAGMA wal_checkpoint(TRUNCATE);"

That took 37.7 s against a 49.6 GB log here. It is lossless — the log's contents
are written into the database, which is what a clean shutdown does anyway.

Everything else

Unchanged from v1.0.0. 1,721 backend tests passing. AGPL-3.0.

v1.0.0 — the open live world, recorded, and yours

Choose a tag to compare

@AndrewCTF AndrewCTF released this 16 Jul 11:44
db86a57

Warning

Superseded by v1.0.1 — do not run this tag.
It records an archive whose write-ahead log grows without bound (49.6 GB against
15.2 GB of data on the box it was found on). Left recording, it fills the disk.
Fixed in v1.0.1; nothing to migrate.

Velocity is a self-hosted situation console. It pulls live aircraft, ships,
satellites, earthquakes, GPS jamming, dark vessels, restricted airspace, naval
warnings, outages and conflict events onto one 3D globe, correlates them on the
server, and keeps the position history on your own disk for as long as you give
it room.

This is the first tagged release. The project has been running daily against
live feeds since June; v1.0.0 marks the point where a stranger can clone it,
run one command, and get the same picture without asking anyone for a key.

Why this exists

Commercial trackers gate the part that matters. Flightradar24 gates replay at 7
days on the free tier, MarineTraffic cut its free history window to 24 hours,
and ADS-B Exchange killed its free API tier outright. A self-hosted tool does
not have that problem: turn on the archive profile and Velocity records position
history to your disk until the disk budget you set runs out. Nobody can
retroactively shorten your window.

What's in the box

  • Live fusion globe — a snapshot measured at 12,647 aircraft (0.5 s old) and
    57,420 vessels while writing this, the vessels MMSI-deduped across four
    keyless sources. About 32,000 of those vessels are moored or parked and
    long-retained, so the moving picture is nearer 25,000. Satellites are
    propagated client-side with real SGP4, not faked motion. Category icons and
    per-entity dossiers, not dots.
  • Replay you own — a disk-backed SQLite archive with a timeline scrubber.
    48-hour rolling window by default, or an open-ended disk-budgeted archive.
  • An investigation layer — a local ontology (objects, assertions with
    provenance, situations, case files), an evidence locker that hashes at
    capture, and case-to-report export.
  • A watch officer — an autonomous pass over the live picture that promotes
    correlated events into incidents with cited reasoning.
  • Workflows and Foundry — bring your own datasets, transforms and builds;
    wire live feeds through Python and SQL blocks into alerts.
  • Keyless alerting — geofence and watch-list rules firing to Discord or a
    generic webhook, server-side, with no browser open.
  • An MCP server — query the live console from an AI agent.

Keyless by default

ADS-B grid, Baltic AIS, MyShipTracking, ShipXplorer, USGS earthquakes, the
Carto basemap and CelesTrak all work with no API key. FIRMS degrades gracefully
without one. Keys are optional everywhere they appear.

Quick start

git clone https://github.com/AndrewCTF/velocity.git
cd velocity
cp .env.example .env       # optional, leave it empty and it still works
docker compose up          # api + web + nginx on :8080

Scope and limits

Read these before you get excited. It is a single-analyst tool, not a team
platform. Live derived state (the current incident list, AOI selections,
watch-officer briefs) lives in memory and clears on restart; what you
deliberately keep is durable — the position archive, the local ontology, and
the evidence locker. There is no facial recognition and there never will be.
AI-drafted prose is labeled as such wherever it reaches a document.

1,719 backend tests pass at this tag, most of them pinning hard-won invariants
about upstream feed behaviour that each regressed at least once.

AGPL-3.0.