Help us shape the future of Go! Take the 2026 Go Developer Survey

rivolt

module
v0.36.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 16, 2026 License: MIT

README

Rivolt

A Rivian companion. Live telemetry, per-trip analytics, a charging cost ledger, and a route planner that estimates what a road trip will cost.

Rivolt is an open-source companion app for Rivian vehicles. It streams live telemetry from your truck, records every drive and charging session against your own electricity rate, and plans road trips with cost / weather / efficiency analysis built in.

Rivolt turns your Rivian's charging and driving into a priced ledger you own, and a planner that accounts for what a trip will cost.


Try the live demo

preview.rivolt.dev/demo is a read-only demo on a generated sample truck: no sign-in, no Rivian account, and no real vehicle data. Browse the Now card, the Money statement and the Activity feed the way you would on your own truck.

  • It runs on the preview environment, so it can reset or be briefly unavailable.
  • The numbers are made up (a home-charging month at $0.16/kWh); yours will differ.
  • To use it with your own truck, sign up directly — no invite needed; see docs/SIGNUP.md.

Overview — see your truck at a glance

The home view stitches together what you actually care about right now: current SoC and range, where the truck is, whether it's plugged in, the most recent drive, the most recent charge, and the rolling weekly totals (miles, kWh, $).

Rivolt overview page


Live — real-time telemetry

The Live page subscribes to Rivian's WebSocket feed and renders 1–5 s frames while you're driving (~30 s when parked). SoC, range, gear, drive mode, charger state, inside / outside temp, tire pressures, and the GPS dot all move in real time. Useful in the passenger seat to verify a route, or from the kitchen to see if the truck actually started charging when you plugged it in.

Live panel


Drives — every trip explained

The Drives page lists every recorded drive with distance, duration, average speed, energy used, cost, and a route preview. Click in to land on the per-drive detail: the map is road-snapped via self-hosted Valhalla and coloured by speed — gray for parking-lot crawls, rose for interstate — over a Protomaps basemap. Below the map, time-aligned charts for speed, SoC, elevation, temperature, headwind, and precipitation; weather context for the drive's start hour pulled from Open-Meteo when enabled.

A "Low GPS accuracy" pill surfaces when the Rivian modem returned stale fixes or implausible jumps during the drive, with thresholds that are admin-tunable so the warning doesn't fire on every parking garage.

Drives list

Drive detail page


Charges — every kWh, every dollar

The Charges page lists every charging session — date, location, energy delivered, peak kW, total cost. Click in for the full detail: power curve over time, peak and average kW, energy delivered, session duration, and — when the Parallax feed is available — the thermal split between energy going to the pack and energy spent on battery conditioning.

Sessions are automatically bucketed Home / Public / DCFC by DBSCAN clustering on coordinates and peak kW; sessions in your home cluster price against your configured home rate, sessions out in the wild price against the session's actual reported cost when Rivian provides it.

Charges list

Charge detail page


Trip planner — plan with cost in mind

This is the feature the app was built for. Pick origin, destination, optional via-stops, and a departure date/time on the calendar. Rivolt asks Rivian's own planner to lay out a route with charging stops, then layers on top of it:

  • A self-hosted Protomaps basemap with the actual route drawn in.
  • A charger overlay along the route corridor — perpendicular distance to the polyline, not bounding box, with arc-length trimming near the endpoints so the destination metro doesn't drag in dozens of irrelevant downtown stations.
  • DCFC / L2 / All toggle on the charger overlay, sourced from NREL AFDC data with proper DCFC vs L2 discrimination (Tesla Destination chargers no longer mis-classified as Supercharger).
  • Last-trip persistence — closing and reopening lands you back on yesterday's setup.
  • Departure-time-aware arrival clock: the table shows arrival HH:MM at every stop based on your chosen departure, not "now".

Trip planner


Trip analysis — what the trip will cost (estimate)

After the route comes back, the Trip analysis card breaks it down. The dollar figures are computed deterministically in Go — not asked of an LLM — from your home rate, stop SoC deltas, and each matched charging network's rate (your member rate where set), with a default only for unknown stations:

  • DCFC spend — estimated spend at fast chargers along the way.
  • Home-rate equivalent — what the total energy used would have cost if every kWh came from your home meter; useful as a "what is this trip costing me" baseline.

Around those numbers, when an AI provider is wired by the operator, the model writes short categorised commentary across Cost, Efficiency (drive mode, departure timing), Weather (only when there's something material — cold-snap, headwind > 15 kph, precipitation), and Vehicle (tire pressures, pack size, adapter dependency at any stop). Empty categories hide their headers.


Settings — everything in one place

A five-tab layout for the per-user controls:

  • Account — Rivian connection (with the optional dedicated Authorized Driver setup documented in docs/SIGNUP.md).
  • Vehicle — vehicle profile (pack capacity), display units, home location.
  • Charging — home $/kWh rate + currency, preferred public networks, trip planner defaults.
  • Notifications — per-device push subscription with per-event toggles (see below).
  • Data — ElectraFi CSV import + danger zone (full JSON backup, reset).

Notifications — web push

Web Push (VAPID), per-device subscribe. Enable once on each device you want pinged, pick which events you care about, and Rivolt delivers an OS-level notification on the next matching event. Currently wired: charging session completes (with the final SoC + kWh added in the body). Plug-in reminders and anomaly alerts have the plumbing — event sources land next.


Sign up

See docs/SIGNUP.md for an end-to-end walkthrough of:

  • signing up directly,
  • connecting your Rivian account (with the dedicated Authorized Driver account approach we recommend),
  • importing your history from ElectraFi,
  • configuring your home charging cost,
  • planning your first trip.

Data ownership

  • Your Rivian credentials are AES-GCM sealed and decrypted only in-memory at request time. Disconnect Rivian in one click.
  • AI features (when enabled by the operator) are powered by an install-wide provider — OpenAI, Anthropic, or Google Gemini — configured from the admin UI. Each user's drive / trip context is sent to that provider only at the moment a recap or trip analysis is generated; nothing is mirrored to a third-party service in between.
  • All your drive, charge, and trip data is exportable as JSON from Settings → Data, and deletable from the same page.
  • Read-only against the Rivian API. Rivolt sends commands to no Rivian endpoint, period. It can't unlock doors, start charging, or change drive mode. Telemetry only.

Stack

  • Single Go binary + embedded SPA. Distroless multi-arch container image; the same binary serves API + static assets.
  • Go 1.25, chi router, pgx-backed database/sql, coder/websocket for the Rivian feed, webpush-go for VAPID.
  • Postgres with Row-Level Security, monthly partitioning on vehicle_state, envelope-encrypted secrets in user_secrets. Multi-tenant from day one: every data-plane store carries user_id, every query filters on it, every request pins app.user_id so RLS does the final enforcement at the DB.
  • React 18 + TypeScript + Vite + Tailwind v3 for the SPA; TanStack Query for the data layer; uPlot for time-series charts; Leaflet + protomaps-leaflet for maps.
  • Optional AI providers — OpenAI, Anthropic, or Google Gemini, selected install-wide by the operator from the admin UI. Hot-swappable; calls go to the chosen provider only when an AI feature actually runs (trip analysis, drive recap).

Status

In production at rivolt.dev. Preview at preview.rivolt.dev tracks main.

See docs/ROADMAP.md for what's queued and docs/ARCHITECTURE.md for the load-bearing design decisions.


License

Core is MIT-licensed. Some future add-ons may ship under a separate commercial license; details will be published alongside each add-on.

"Rivian" is a trademark of Rivian Automotive, Inc. Rivolt is an independent, community-built project with no affiliation to, endorsement by, or partnership with Rivian. Reference to Rivian is for descriptive purposes only.

Use at your own risk. Rivolt relies on unofficial access to Rivian's APIs and may break at any time.

Directories

Path Synopsis
cmd
replay command
Command replay runs a captured frame stream (JSONL) through the recorder offline and prints the drives it produces — a hand-debugging companion to the replay-harness unit tests.
Command replay runs a captured frame stream (JSONL) through the recorder offline and prints the drives it produces — a hand-debugging companion to the replay-harness unit tests.
rivolt command
Command rivolt is the single-binary server for the Rivolt Rivian companion.
Command rivolt is the single-binary server for the Rivolt Rivian companion.
sampletruck command
Command sampletruck writes the generated sample-truck corpus (A16, docs/product/sample-truck.md) as replay-format JSONL, one frame per line, and prints a summary to stderr.
Command sampletruck writes the generated sample-truck corpus (A16, docs/product/sample-truck.md) as replay-format JSONL, one frame per line, and prints a summary to stderr.
internal
ai
Package ai hosts the hot-swappable LLM provider adapters (OpenAI, Anthropic, Google Gemini) used across Rivolt: weekly digest, trip planning, charging strategy, and anomaly explanations.
Package ai hosts the hot-swappable LLM provider adapters (OpenAI, Anthropic, Google Gemini) used across Rivolt: weekly digest, trip planning, charging strategy, and anomaly explanations.
aibudget
Package aibudget enforces a per-user daily cap on LLM-backed calls (trip advice, drive efficiency).
Package aibudget enforces a per-user daily cap on LLM-backed calls (trip advice, drive efficiency).
analytics
Package analytics hosts local, deterministic analyses over the user's driving and charging history.
Package analytics hosts local, deterministic analyses over the user's driving and charging history.
api
Package api wires the HTTP surface for Rivolt.
Package api wires the HTTP surface for Rivolt.
appsettings
Package appsettings is the install-wide key/value store backing the AI provider configuration (and any future install-scoped setting).
Package appsettings is the install-wide key/value store backing the AI provider configuration (and any future install-scoped setting).
auth
Package auth owns Rivolt's session-level authentication: the /api/auth/* endpoints, the cookie format, and the middleware that resolves an authenticated user into request context.
Package auth owns Rivolt's session-level authentication: the /api/auth/* endpoints, the cookie format, and the middleware that resolves an authenticated user into request context.
chargers
Package chargers serves the chargers.pmtiles archive from the rivolt API: the entire ~10 MB file is fetched once at startup (or first request), parsed in-memory, and queries against it run fully server-side.
Package chargers serves the chargers.pmtiles archive from the rivolt API: the entire ~10 MB file is fetched once at startup (or first request), parsed in-memory, and queries against it run fully server-side.
charges
Package charges persists derived charge sessions.
Package charges persists derived charge sessions.
config
Package config holds runtime configuration for rivolt.
Package config holds runtime configuration for rivolt.
crypto
Package crypto is Rivolt's in-process key-wrapping layer.
Package crypto is Rivolt's in-process key-wrapping layer.
db
Package db owns the Rivolt Postgres connection pool, the shared migration runner, and a handful of identity helpers used by every store.
Package db owns the Rivolt Postgres connection pool, the shared migration runner, and a handful of identity helpers used by every store.
dcfcrates
Package dcfcrates owns the DCFC network table used by trip planning.
Package dcfcrates owns the DCFC network table used by trip planning.
demo
Package demo seeds and identifies the public read-only demo account (B7).
Package demo seeds and identifies the public read-only demo account (B7).
drain
Package drain computes the fleet's parked-drain metrics from the persisted vehicle_state history and exposes them to Prometheus (D1, the instrument for the battery-drain experiment's Gate A).
Package drain computes the fleet's parked-drain metrics from the persisted vehicle_state history and exposes them to Prometheus (D1, the instrument for the battery-drain experiment's Gate A).
drives
Package drives persists derived drive sessions.
Package drives persists derived drive sessions.
electrafi
Package electrafi imports TeslaFi / ElectraFi polling-dump CSV files into Rivolt's drives and charges stores.
Package electrafi imports TeslaFi / ElectraFi polling-dump CSV files into Rivolt's drives and charges stores.
elevation
Package elevation looks up altitude (meters above sea level) for arbitrary lat/lon coordinates against the Mapzen Terrarium DEM (https://github.com/tilezen/joerd/blob/master/docs/formats.md#terrarium).
Package elevation looks up altitude (meters above sea level) for arbitrary lat/lon coordinates against the Mapzen Terrarium DEM (https://github.com/tilezen/joerd/blob/master/docs/formats.md#terrarium).
email
Package email sends transactional mail via the Resend HTTP API.
Package email sends transactional mail via the Resend HTTP API.
flags
Package flags owns the DB-backed operational flag table (migration 0003_flags.sql).
Package flags owns the DB-backed operational flag table (migration 0003_flags.sql).
geocoding
Package geocoding wraps Open-Meteo's free geocoding endpoint (https://geocoding-api.open-meteo.com/v1/search) for forward- geocoding text input → lat/lon.
Package geocoding wraps Open-Meteo's free geocoding endpoint (https://geocoding-api.open-meteo.com/v1/search) for forward- geocoding text input → lat/lon.
hydra
Package hydra is a thin admin-API client for Ory Hydra.
Package hydra is a thin admin-API client for Ory Hydra.
identity
Package identity holds the two install-wide switches that decide how much an email address is trusted when it identifies an account.
Package identity holds the two install-wide switches that decide how much an email address is trusted when it identifies an account.
idp
Package idp defines the UserProvider interface — the single contract between Rivolt's API layer and whichever identity backend is deployed.
Package idp defines the UserProvider interface — the single contract between Rivolt's API layer and whichever identity backend is deployed.
kratos
Package kratos provides admin-side provisioning of Ory Kratos identities via the Kratos admin API.
Package kratos provides admin-side provisioning of Ory Kratos identities via the Kratos admin API.
leases
Package leases coordinates per-vehicle ownership across multiple rivolt pods so each vehicle is subscribed by exactly one process.
Package leases coordinates per-vehicle ownership across multiple rivolt pods so each vehicle is subscribed by exactly one process.
ledger
Package ledger builds the server-side money statement (the Money tab and, eventually, every cost/energy total in the SPA) from recorded charges and drives.
Package ledger builds the server-side money statement (the Money tab and, eventually, every cost/energy total in the SPA) from recorded charges and drives.
logging
Package logging provides a context-aware slog handler and helpers for stamping per-request fields (request_id, user_id, vehicle_id, trace_id) onto every log line emitted while serving a request, without requiring callers to thread the logger through.
Package logging provides a context-aware slog handler and helpers for stamping per-request fields (request_id, user_id, vehicle_id, trace_id) onto every log line emitted while serving a request, without requiring callers to thread the logger through.
maps
Package maps wires same-origin reverse-proxies that let the SPA reach self-hosted map services (Valhalla for routing + map matching, an nginx-served PMTiles bundle for vector basemaps) through the existing rivolt origin and session cookie.
Package maps wires same-origin reverse-proxies that let the SPA reach self-hosted map services (Valhalla for routing + map matching, an nginx-served PMTiles bundle for vector basemaps) through the existing rivolt origin and session cookie.
metrics
Package metrics owns the application's Prometheus instrumentation.
Package metrics owns the application's Prometheus instrumentation.
oidc
Package oidc adds an OIDC sign-in path alongside the static operator credential and the trusted-proxy header in internal/auth.
Package oidc adds an OIDC sign-in path alongside the static operator credential and the trusted-proxy header in internal/auth.
packhealth
Package packhealth derives an "effective pack capacity" per qualifying charge session.
Package packhealth derives an "effective pack capacity" per qualifying charge session.
push
Package push handles Web Push subscriptions and notification delivery.
Package push handles Web Push subscriptions and notification delivery.
ratelimit
Package ratelimit implements the global upstream token bucket gating outbound calls to Rivian's GraphQL gateway.
Package ratelimit implements the global upstream token bucket gating outbound calls to Rivian's GraphQL gateway.
recap
Package recap turns a finished drive into a short natural-language narration ("Trip recap") via the operator-configured LLM provider.
Package recap turns a finished drive into a short natural-language narration ("Trip recap") via the operator-configured LLM provider.
repair
Package repair holds one-off data repairs run from the rivolt CLI.
Package repair holds one-off data repairs run from the rivolt CLI.
repair/phantom
Package phantom is the repair for phantom (stale-copy) live charges: rows the recorder fabricated from a replayed previous-session total, with no power and no SoC change behind them (architect's scratchpad/phantom-charges.md; devils-advocate's audit).
Package phantom is the repair for phantom (stale-copy) live charges: rows the recorder fabricated from a replayed previous-session total, with no power and no SoC change behind them (architect's scratchpad/phantom-charges.md; devils-advocate's audit).
repair/r2
Package r2 rebuilds the drives and charges of a vehicle whose vehicleState gear was unusable (R2 before #214: the gear stayed at the last D/R for days, so drives never closed and no charge opened).
Package r2 rebuilds the drives and charges of a vehicle whose vehicleState gear was unusable (R2 before #214: the gear stayed at the last D/R for days, so drives never closed and no charge opened).
rivian
Package rivian is Rivolt's adapter to the unofficial Rivian owner-app GraphQL API.
Package rivian is Rivolt's adapter to the unofficial Rivian owner-app GraphQL API.
samples
Package samples persists raw vehicle_state polling snapshots.
Package samples persists raw vehicle_state polling snapshots.
secrets
Package secrets is the persistence layer on top of [`internal/crypto`](../crypto).
Package secrets is the persistence layer on top of [`internal/crypto`](../crypto).
sessions
Package sessions is the source-of-truth store for authenticated user sessions.
Package sessions is the source-of-truth store for authenticated user sessions.
settings
Package settings persists app-level configuration (AI provider keys, charging cost, Rivian session) in the shared Postgres database.
Package settings persists app-level configuration (AI provider keys, charging cost, Rivian session) in the shared Postgres database.
signupgate
Package signupgate implements RIVOLT_SIGNUP_DISABLED: an install-wide switch that closes every path that can create a new account on its own (the /signup page and API, access requests, OIDC and Kratos first sign-in).
Package signupgate implements RIVOLT_SIGNUP_DISABLED: an install-wide switch that closes every path that can create a new account on its own (the /signup page and API, access requests, OIDC and Kratos first sign-in).
signuprequests
Package signuprequests manages the pre-account waitlist: someone visits /signup without an invite, fills the "request access" form, and lands as a pending row that an admin later approves (minting a single-use magic-link signup token) or rejects.
Package signuprequests manages the pre-account waitlist: someone visits /signup without an invite, fills the "request access" form, and lands as a pending row that an admin later approves (minting a single-use magic-link signup token) or rejects.
statement
Package statement builds and sends the monthly statement email (scratchpad/statement-email-spec.md): pure rendering from the ledger's own Monthly, the signed unsubscribe token, the preference and send-state store, and the idempotent hourly job.
Package statement builds and sends the monthly statement email (scratchpad/statement-email-spec.md): pure rendering from the ledger's own Monthly, the signed unsubscribe token, the preference and send-state store, and the idempotent hourly job.
tracing
Package tracing wires the OpenTelemetry trace SDK with an OTLP/HTTP exporter pointed at Grafana Tempo.
Package tracing wires the OpenTelemetry trace SDK with an OTLP/HTTP exporter pointed at Grafana Tempo.
tripadvice
Package tripadvice wraps the operator-configured AI provider to generate plain-language observations about a Rivian trip plan: charging stop summary, arrival SoC commentary, adapter warnings, and drive-mode suggestions when the plan looks sub-optimal.
Package tripadvice wraps the operator-configured AI provider to generate plain-language observations about a Rivian trip plan: charging stop summary, arrival SoC commentary, adapter warnings, and drive-mode suggestions when the plan looks sub-optimal.
tripmultiday
Package tripmultiday orchestrates a multi-day trip plan over Rivian's planTripWithMultiStopV2.
Package tripmultiday orchestrates a multi-day trip plan over Rivian's planTripWithMultiStopV2.
tripprofile
Package tripprofile derives an energy-consumption multiplier from a user's editable VehicleProfile (wheel size, tire type, roof / rear accessories, default cargo).
Package tripprofile derives an energy-consumption multiplier from a user's editable VehicleProfile (wheel size, tire type, roof / rear accessories, default cargo).
trips
Package trips persists per-user named trip templates for the planner.
Package trips persists per-user named trip templates for the planner.
tripweather
Package tripweather corrects a Rivian trip plan's per-waypoint SoC curve for weather conditions Rivian's planner doesn't see.
Package tripweather corrects a Rivian trip plan's per-waypoint SoC curve for weather conditions Rivian's planner doesn't see.
weather
Package weather enriches a drive with the conditions at its start point and time.
Package weather enriches a drive with the conditions at its start point and time.
web
Package web embeds the built Vite web app into the Go binary.
Package web embeds the built Vite web app into the Go binary.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL