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

rivolt

module
v0.36.11 Latest Latest
Warning

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

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

README

Rivolt

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

Rivolt is an open-source companion app for Rivian vehicles. It shows your truck's live state, 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 demo

demo.rivolt.dev is a stable, read-only demo on a generated sample truck: no sign-in, no Rivian account, and no real vehicle data. Browse every tab below the way you would on your own truck, and open the planner's sample trips to see a route with charging stops.

  • 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.

Five tabs

Rivolt is organised around five tabs: Now, Activity, Money, Plan and Truck. Now, Activity and Truck share one calendar scope (Week, Month, Year, All or a custom range), Money steps month by month, and the app follows your system light / dark setting (or the Appearance card in Settings).

Now — the truck at a glance

The current state on one card: battery as an arc with the charge limit marked on it, range, and whether the truck is parked, plugged in, charging or driving (live frames while it moves, slower when parked). Under it, the numbers for the scope you picked: miles since the last charge, miles, energy added, efficiency, cost, and an Idle awake tile that links to Truck. Below those, miles per day and a battery trend with charging and driving shaded behind the line, so you can see why the percentage moved.

Now

Activity — every drive and charge

One day-grouped Feed of drives and charges, newest first, with Table and Map views of the same rows (the map shows Drives or Charges one layer at a time). Open any row for its detail page.

Drives. Distance, duration, average speed, energy used and cost. The detail page draws the road-snapped route (self-hosted Valhalla over a Protomaps basemap) coloured by speed, with time-aligned charts for speed, SoC, elevation, temperature, headwind and precipitation, and weather context for the drive's start hour from Open-Meteo when enabled. A "Low GPS accuracy" pill appears when the modem returned stale fixes or implausible jumps; its thresholds are admin-tunable.

Charges. Date, location, energy delivered, peak kW and cost. The detail page has the power curve, peak and average kW, session duration and, when the Parallax feed is available, the thermal split between energy into the pack and energy spent on battery conditioning. Sessions are bucketed Home / Public / Fast by clustering on coordinates and peak kW; home sessions price against your configured home rate, others against the cost Rivian reports.

Activity

Money — what charging actually cost

A monthly statement: spend for the month, how it compares with the last one, and where the energy came from (Home, Away, DCFC) in dollars or kWh. Receipts list every session with its price and where that price came from, and anything estimated or unpriced is flagged rather than guessed (with a one-tap way to add the price). "About these numbers" spells out what is and is not included in the total.

Money

Plan — a map-first trip planner

This is the feature the app was built for. On a computer the map fills the right of the page and stays put while you work on the left; on a phone the map is on top and the trip sits in a sheet you drag up.

  • Route bar. From and To (with a swap button), departure, starting SoC on a slider, and a Plan button. Via-stops, target arrival SoC, drive mode and the adapter switch live under "More options".
  • Result. Rivolt asks Rivian's own planner for a route with charging stops and shows arrival SoC, trip cost, time, distance and charging time, with alternatives as a compact chooser.
  • Itinerary. Start, every charging stop and arrival as a vertical timeline. Open a stop for the charger and network, SoC in and out, charge time, energy and cost, and see it highlighted on the map (and the reverse).
  • Map. Self-hosted Protomaps basemap with the route, plus a charger overlay along the route corridor (distance to the polyline, not a bounding box) with DCFC / L2 / Hotels filters from NREL AFDC data.
  • Cost. Dollar figures come from your home rate, stop SoC deltas and each matched network's rate (your member rate where set), not from an LLM. When the operator wires an AI provider, short commentary on cost, efficiency, weather and the vehicle is added.
  • Saved and Plan vs actual. Planner, Saved and Plan vs actual are tabs at the top; after you drive a saved trip the app compares plan and reality (cost, energy, stops, arrival battery, duration).

Plan

Truck — health, sleep and drain

Parked drain, pack capacity and the lowest tire pressure and software version the truck reports. The sleep chart shows idle-awake time per day (the car awake while parked and not charging), with a warning when it keeps waking up again within a minute.

Truck

On a phone, and in light

The same tabs work at phone width, and the main tabs have a light theme.

Plan on a phone Now on a phone

Now in light


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 19 + TypeScript + Vite + Tailwind v4 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. The demo at demo.rivolt.dev runs on generated data, and a 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.
insights
Package insights computes read-only breakdowns of the user's own recorded data.
Package insights computes read-only breakdowns of the user's own recorded data.
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.
otadetails
Package otadetails keeps each vehicle's OTA release details (current and available software version, release-notes link) fresh from Rivian's getOTAUpdateDetails: once a day per vehicle and when a monitor connects, never per request.
Package otadetails keeps each vehicle's OTA release details (current and available software version, release-notes link) fresh from Rivian's getOTAUpdateDetails: once a day per vehicle and when a monitor connects, never per request.
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.
receipts
Package receipts syncs what Rivian actually billed for each charging session (its Charging history) next to our own estimate.
Package receipts syncs what Rivian actually billed for each charging session (its Charging history) next to our own estimate.
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).
retention
Package retention runs the daily sweeps that delete personal data once it has outlived its purpose: expired sessions (IP and User-Agent) and waitlist requests.
Package retention runs the daily sweeps that delete personal data once it has outlived its purpose: expired sessions (IP and User-Agent) and waitlist requests.
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.
vehiclelease
Package vehiclelease holds a vehicle's lease terms and computes where the driver stands against the mileage allowance.
Package vehiclelease holds a vehicle's lease terms and computes where the driver stands against the mileage allowance.
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