Skip to content

Releases: getvelox/velox

v0.8.2

Choose a tag to compare

@sagarsuperuser sagarsuperuser released this 18 Oct 04:35

Fixes before the first partner installs. Long invoice PDFs go onto more pages, and every email has a Date and a Message-ID. Webhook URLs with a private IPv6 address are refused when saved, and sign-ins record the real client address. No database migrations.

Before you upgrade

  • No migrations in this release. The database stays at 0180, as in v0.8.0 and v0.8.1. Take a backup as for any upgrade.
  • OTEL_TRACES_SAMPLER now takes effect. If you set it, Velox keeps only the traces it selects; before, every trace was exported. Unset, nothing changes. The Compose stack now passes OTEL_TRACES_SAMPLER and OTEL_TRACES_SAMPLER_ARG from .env. (#1206)
  • A webhook endpoint saved earlier with a private IPv6 URL still fails every delivery. Change its URL to a public address. (#1199)

What changed

Invoices

  • Long invoice and credit-note PDFs go onto more pages. Before, rows after about the 30th and the totals were drawn below the page edge. Later pages repeat the table header and are numbered "Page N of M". Amounts are unchanged. (#1172)
  • Thai baht amounts on PDFs print as THB 1,234.00; the PDF font has no ฿ sign. (#1172)

Email

  • Every email has a Date and a Message-ID on your SMTP_FROM domain, and accented names and long lines travel as quoted-printable in short lines. The wording is unchanged. (#1187)

Security

  • Dashboard sign-ins and invite accepts record the client address TRUST_PROXY resolves, not the first X-Forwarded-For entry, which any client can write. (#1203)
  • Webhook endpoint create and update refuse private IPv6 addresses such as fd00::1, using the same check as delivery. (#1199)

Self-hosting

  • A failed release of an Idempotency-Key is logged at ERROR and counted in velox_idempotency_cache_errors_total{stage="release"}. (#1206)
  • deploy/compose/.env.example gives the tracing endpoint on the OTLP/HTTP port 4318. (#1206)

Every change, with its reasoning: CHANGELOG.

v0.8.1

Choose a tag to compare

@sagarsuperuser sagarsuperuser released this 17 Oct 15:29
5df594e

Safer upgrades: a repair command for a database upgrade that stopped midway, and a limit on how long a migration waits for a table lock. Also a faster invoice list, and the Compose stack passes the Postmark webhook login and VELOX_API_BASE_URL. No database migrations.

Before you upgrade

  • No migrations in this release. The database stays at 0180, the last migration of v0.8.0. Take a backup as for any upgrade.
  • A migration waits at most 5 seconds for a table lock, then stops. This applies to later upgrades: while a migration waits for a lock, every other query on that table waits behind it. A migration that runs in one transaction leaves nothing behind when it stops, and the error says to run the upgrade again. MIGRATION_LOCK_TIMEOUT changes the wait (for example 30s, or 0 for no limit). Index builds marked CONCURRENTLY have no limit. Steps: runbook. (#1167)
  • A dirty schema_migrations has a named error and a repair command. Velox still refuses to start or migrate. The error now names the migration, says whether some of its changes may be in the database, and gives the velox migrate force <version> commands to run. velox migrate status prints the same advice. Steps: runbook. (#1165)
  • Compose reads POSTMARK_WEBHOOK_USER, POSTMARK_WEBHOOK_PASS, VELOX_API_BASE_URL and MIGRATION_LOCK_TIMEOUT from .env. If you passed them with an override file or by editing docker-compose.yml, move them to .env. (#1164)

What changed

Upgrades

  • velox migrate force <version> clears the dirty mark a failed migration leaves. It refuses while a migration is running, when nothing is dirty, and for a version that is not next to the dirty one. (#1165)
  • Each migration session sets a Postgres lock_timeout (5 seconds unless MIGRATION_LOCK_TIMEOUT says otherwise). CI now fails a new migration that builds an index or adds a constraint the write-blocking way. (#1167)

Dashboard

  • The invoice list reads each banner fact (card on file, Stripe connection, dunning, pause, payment page) once per page instead of once per row. Banners are unchanged. (#1166)

Self-hosting

  • The Compose stack passes the Postmark bounce webhook login and VELOX_API_BASE_URL through to Velox, and deploy/compose/.env.example lists them. (#1164)
  • Measured sizing for one small server: a €6.49/month Hetzner CX23 took about 1 million events a day with 0 errors (scale). The one-VM production guide got fixes from a first walk through it. (#1163)

Every change, with its reasoning: CHANGELOG.

v0.8.0

Choose a tag to compare

@sagarsuperuser sagarsuperuser released this 17 Oct 12:29
c8b2013

LiteLLM usage for an unknown customer is held instead of lost, and new accounts retry failed payments. Also: Settings → Email with a test send, a dashboard that says what each page is for, and dates that follow the billing timezone. Three database migrations.

Before you upgrade

  • Run migrations first, as for any upgrade. This release adds three: 0178 (a column on Stripe credentials), 0179 (a new usage_held_events table) and 0180 (a column on dunning policies). None rewrites existing rows.
  • New accounts start with a "Default retries" dunning policy; existing accounts do not get one. An account created after the upgrade retries a failed payment on day 3, 7 and 14, then leaves the subscription and invoice alone. An account that had no default policy before the upgrade still retries nothing until you create one; the Dunning pages now say so. (#1155)
  • LiteLLM usage for a customer or meter that does not exist yet is held, not dropped. The callback still answers 200, and its response gains a held count. Held rows are listed on the Usage page and at GET /v1/usage-events/held. Create the customer, then release them. Released usage from before the customer's subscription started is stored but not invoiced. (#1151)
  • Email actions answer 409 email_not_configured when SMTP_HOST is not set. This covers sending an invoice or credit note, sending a payment-method setup link and inviting a teammate. They used to report "sent" while the queued email failed. Save & send still finalizes the invoice. Password reset is unchanged.
  • A charge Stripe never answered returns payment_outcome_unknown, not provider_error. A decline is still provider_error. If you branch on the code, add the new one.
  • Due dates and trials count calendar days in the billing timezone. "Net N" and trial_days added N×24 hours, so across a daylight-saving change a due date printed one day early and a trial ended an hour late. Dates that do not cross a clock change are the same as before.
  • A date-only to filter now ends at 23:59:59.999999 UTC (was 23:59:59.000), so CSV exports, the invoice list and the audit log include the last second of that day.
  • Many messages were reworded; codes are unchanged. attention.message on an invoice is now up to three short lines, and the longer explanation moved to attention.detail. Subscription, pricing, recipe, webhook and password refusals read plainly. Match on reason and code, not on the text.
  • Set your company name if you have not (Settings → Business details). With none set, PDFs no longer print "Velox" as the seller. That includes already-issued invoices, because a PDF is rendered when it is opened. Totals and lines do not change. (#1093)

What changed

LiteLLM and usage

  • Usage for an unknown customer or meter is kept, shown on the Usage page and released once, under its original key. Discard marks rows as never billed, even if LiteLLM resends them. (#1151)
  • The Usage page shows each meter's value by its own aggregation, so a Maximum meter no longer adds up its readings. (#1097)
  • Export CSV downloads every filtered row from the server (it stopped at 50,000), up to 365 days per file. GET /v1/exports/usage-events.csv takes customer_id, meter_id and dimensions. (#1123)

Dunning and payments

  • New accounts retry failed payments from day one with "Default retries". Your first enabled policy takes over as the default. (#1155)
  • Charge now on an invoice paid entirely from credit closes its dunning run. (#1061)
  • The customer payment page no longer shows a Pay button when Stripe is not connected; it says who to contact. (#1118)
  • Stripe key errors say whether Stripe refused the key or could not be reached (last_verified_error_kind). (#1067)

Billing and dates

  • The next-invoice preview prorates like the invoice: a calendar first period, an added item and a quantity change show prorated lines. (#1096)
  • Net N due dates and trial ends count calendar days in the billing timezone. New manual invoices store the account's timezone, so their PDFs print local dates. (#1077, #1087, #1081)
  • Dashboard charts bucket by UTC day whatever the database server's time zone. (#1070)
  • A margin request without dates follows the customer's test clock, and customer usage accepts a full leap year. (#1082, #1088)
  • A new trial no longer hides the running cycle's usage. (#1073)
  • Draft PDFs carry a DRAFT mark, and no PDF prints "Velox" as the seller when no company name is set. (#1093)

Settings and email

  • Settings → Email shows whether this server can send email and sends a test email with the mail server's real answer. API: GET /v1/settings/email and POST /v1/settings/email/test. (#1121)
  • GET /v1/whoami gains smtp_configured, can_send_invites and can_email_setup_links, and the Team tab says when invites are off.

Dashboard

  • The customer Margin card shows each model's margin next to its billed amount and cost. API: each row of the margin response's per-model breakdown gains margin_bps. The demo now bills two models, so it shows two rows. (#1159)
  • A task-grouped sidebar, one notice per page with wording that matches what billing does, and amounts in each invoice's or customer's own currency. (#1062 to #1067, #1091, #1092)
  • A new account's Home explains Velox and opens a "Bill your first customer" checklist, and each page says what it is for. (#1138, #1145)
  • Credits on the customer page show granted, used, expired, canceled and deducted amounts. (#1098)
  • Margin shows "—" with the missing setup when no provider cost is known, instead of 100%. (#1095)
  • Pages fit a 390px phone screen.

Docs

  • New pages: a first-invoice tutorial, "How Velox works", "Production on one VM" and a quick-start page for each demo step. The operations runbook is one page per problem.

Every change, with its reasoning: CHANGELOG.

v0.7.0

Choose a tag to compare

@sagarsuperuser sagarsuperuser released this 14 Oct 17:15
bfa79fa

A one-command Docker install, late usage billed, issued invoices that never change, a complete API reference, and fixes from a pre-launch check. One database migration.

Before you upgrade

  • Every usage event needs its own idempotency_key. POST /v1/usage-events, /batch and /backfill refuse an event without one, or with one longer than 255 characters, with 422. Pick one key per event and send the same key when you retry it. The Idempotency-Key header does not count. The LiteLLM integration already sends keys.
  • Invoices are built 1 to 2 hours after a period ends (was within an hour), so usage that arrives a little late is billed. Webhooks, charges and dunning move later by the same amount; invoice dates do not. Set INVOICE_GRACE_PERIOD=0 for the old timing.
  • A scheduled cancel takes effect up to an hour later. The subscription stays active until then, though canceled_at is still the date you set.
  • Removing a meter's last price can be refused (409) while a plan with a live subscription uses that meter.
  • Run migrations first, as for any upgrade. This release adds four columns.

What changed

Install

  • deploy/compose/quickstart.sh starts Velox from the release images with only Docker and git, prints a login and a test key, and ./quickstart.sh demo runs token usage through to an invoice. (#959)
  • Login works in Safari when Velox runs at http://localhost.
  • docker ps no longer shows healthy containers as unhealthy, and nginx follows the API after it is recreated.

Billing

  • Usage that arrives within the grace window after a period ends is billed on that period's invoice. (#961)
  • An issued invoice or credit note keeps the customer and company details it was issued with. Editing them later no longer changes old PDFs or the hosted page. (#964)
  • Several changes made just after a period ended, before its invoice, acted on the wrong period. They now apply to the new one. (#961)
  • Usage that matches no price is counted and logged, including on spend-threshold invoices. (#981)
  • A retried usage event without a key was billed twice. Keys are now required. (#980)

Payments

  • A void, write-off or bank payment waits for a card charge in progress. (#975)
  • A Stripe rate limit during a charge no longer parks the invoice. (#978)
  • A card payment and its Stripe webhook arriving together no longer deadlock. (#989)

Margin and LiteLLM

  • Provider cost no longer counts prompt-cache reads twice for LiteLLM calls that send cost_breakdown.
  • The margin card and API name their currencies and do not compute a margin across two currencies.

API

  • The API reference documents 175 operations, up from 68: every route you call. (#962)
  • An unknown dunning policy answers 404, and error responses carry type and code in the right fields.

Docs

  • About 80 sentences across the README and the main docs now claim only what the code does.

Every change, with its reasoning: CHANGELOG.

v0.6.1

Choose a tag to compare

@sagarsuperuser sagarsuperuser released this 13 Oct 17:10
83a034d

A small release that corrects the live credit figure and adds a way to read it from your backend. No database migration, and nothing to do when upgrading.

What was wrong

The public cost page shows a customer how much prepaid credit they have left (estimated_remaining_cents). In 0.5.0 and 0.6.0 that figure was wrong in five situations:

  • After a billing-threshold invoice: usage the invoice had already taken from credit was subtracted a second time. The figure read too low, often zero.
  • Around a trial: usage that is never billed was subtracted in some setups.
  • Several subscriptions on different billing dates: one subscription's dates were applied to all of them.
  • After a plan change in the middle of a period: usage from before the change was dropped.
  • Credit past its expiry date: still counted for up to an hour.

For a customer with one ordinary subscription the figure was right. No invoice and no real balance was affected, only this estimate.

The figure now comes from the same calculation that builds the invoice, so what it subtracts is what the invoice will take.

What is new

  • GET /v1/credits/balance/{customer_id}/live returns the live figure with a secret key: the credit a customer can spend, the usage not yet invoiced, and what is left.
  • A guide: Stop usage when credit runs out. Poll the read on an interval, cache it, and check your cache before serving a request. Velox records usage and never refuses it, so the stop happens in your code.
  • The cost page's credits object gains unbilled_usage_cents, so its three numbers add up.

Worth knowing

  • The live read adds up the customer's usage for the period, so it takes longer as usage grows: about 0.2 seconds at 100,000 events and about 2 seconds at 1,000,000, measured on one laptop. Poll it; do not call it on every request.
  • It is an estimate. It leaves out tax, a base fee billed at the end of the period, and unpaid invoices. The guide lists what it includes and what it does not.
  • The credit.balance_low, credit.balance_depleted and credit.balance_recovered webhooks follow the settled balance, which moves when an invoice is issued. They do not follow usage. Webhooks on the live figure are tracked in #1019.

Every change, with its reasoning: CHANGELOG.

v0.6.0

Choose a tag to compare

@sagarsuperuser sagarsuperuser released this 13 Oct 12:54
1397daf

Fixes from a security review and a billing audit, plus dashboard screens for subscriptions whose billing is failing. No database migration.

Before you upgrade

  • Set METRICS_TOKEN if you scrape /metrics in production. With APP_ENV=production and no token, /metrics now answers 401. The Compose stack runs in production mode.
  • Check three settings. Velox now refuses to start on an APP_ENV such as prod (it must be production), a TRUST_PROXY entry that is not an IP or CIDR, or a DB_* pool setting that is not a whole number.
  • GET /health/ready no longer fails when the billing scheduler stalls. It answers 503 only while draining or when the database ping fails. Alert on velox_scheduler_last_run_timestamp_seconds instead.
  • A cancel can answer 409 unbilled_period when a billing period has ended and cannot be invoiced. Retry billing, then cancel again. If you script cancels, handle this code.
  • Several endpoints answer 404 or 422 for an id that is not yours, where they used to answer 2xx.
  • Webhook endpoints in 100.64.0.0/10 are refused. Some private overlay networks use that range.
  • Images are signed with cosign 3. Verifying an image from this release needs cosign 3 or newer.

What changed

Billing

  • A subscription cancelled after its billing period ended, and before the billing run invoiced it, is now billed for that period. Before, that period's usage and base fee were never invoiced. (#991)
  • Cancelling just after a renewal no longer voids the previous period's usage on a plan billed in advance.
  • Usage ingest answers 503, not 422 "not found", when the database cannot look up the customer or meter. A client that treats a 4xx as final dropped that usage.

Dashboard

  • Subscriptions whose billing is failing are marked in the list, with a Billing failing filter. The subscription page shows the reason, which attempt failed, when the next one is, and a Retry billing button. (#960)
  • A price box left blank no longer saves a $0 rate.
  • A failed load on the customer page shows an error with Retry, not "$0.00" or a blank setup form.

Security

  • Eleven endpoints accepted the id of an object in another tenant, or in the same tenant's other mode (live or test), and wrote a row under the caller's tenant pointing at it. They now refuse it and write nothing. The customer's owner got no spendable credit from this. The changelog lists the endpoints and says how to find rows written earlier.
  • Webhook deliveries check the address they connect to, not only the host name.
  • /metrics requires a bearer token in production.
  • The readiness check no longer returns database error text.
  • A sent email's database row no longer keeps its link token or PDF. Rows from before this release are not cleaned up.
  • A failure at the end of an API key rotation answers a logged 500, not a 422 carrying the error text.
  • The dashboard image is now scanned and signed, and moves from nginx 1.27 to 1.30.

Email

  • A network error while sending no longer marks the customer's address as bounced.

Docs

  • Billing thresholds are described as what they are: an early invoice, once per period, at the next hourly billing run. They do not stop usage. The page said "hard cap".

Every change, with its reasoning: CHANGELOG.

v0.5.0 — LiteLLM now records usage; failing subscriptions are saved and retryable

Choose a tag to compare

@sagarsuperuser sagarsuperuser released this 12 Oct 19:37
84408e5

One day after 0.4.0, with one fix that matters to anyone using LiteLLM, two requests from a user, and the first of the audit fixes.

If you use the LiteLLM integration, read this first

Before this release, Velox recorded nothing from a real LiteLLM proxy. A proxy logs the caller as end_user and puts token counts at the top level of each record. Velox read only user and usage, so it refused every record inside a 200 response. It now reads both shapes.

  • Usage sent before you upgrade was not stored and cannot be recovered from Velox.
  • The guide was checked against LiteLLM 1.83.9 with a real proxy: LiteLLM integration.

Behaviour changes

  • POST /v1/billing/run skips a subscription whose billing is failing until its wait has passed. Use the new retry endpoint to try one now.
  • velox_billing_due_subscriptions and velox_billing_oldest_due_age_seconds no longer count failing subscriptions. The new velox_billing_failing_subscriptions does. Update alerts that relied on the old meaning.
  • Run migrations first, as for any upgrade. This release adds five columns to subscriptions.

What changed

Billing

  • A subscription whose billing fails now keeps a saved record: how many attempts failed, when, and when the next attempt is due. It waits 15 minutes, then 1 hour, 4 hours, 12 hours, then a day at a time, and is set aside after eight failures.
  • POST /v1/subscriptions/{id}/retry-billing clears the failure and bills at once. The retry is written to the audit log.
  • GET /v1/subscriptions?billing=failing lists the failing ones.
  • A lost database connection or a shutdown mid-run does not count as a failure.
  • A panic while billing one subscription no longer stops every later billing run.

Credits

  • GET /v1/public/cost-dashboard/{token} now returns a credits object: the balance, what is left of the prepaid commit and of promotional credit, and an estimate of what remains after this period's usage so far. A customer-facing page can show a balance without a secret key. Requested in #86.

Invoices

  • The buyer's tax ID no longer prints on top of the tax line in the invoice PDF. It appears once, under the buyer's name. Reported in #164.

Running it

  • Release images are built for ARM as well as Intel/AMD, so docker pull works on Apple Silicon Macs and ARM servers.
  • Dashboard build tooling updated; 28 of 35 dependency advisories cleared.

Docs

  • Nine sentences that claimed more than had been shown are corrected, including the README line about several businesses in one install.
  • Thanks to @KeuqS for correcting two claims on the features page (#990).

Every change, with its reasoning: CHANGELOG.

v0.4.0 — the October money-path fixes; two or more replicas supported

Choose a tag to compare

@sagarsuperuser sagarsuperuser released this 12 Oct 10:59
8409735

The first release since August. It carries every money-path fix from September and October, and makes running two or more replicas the supported production setup.

Before you upgrade from v0.3.2 or earlier

  • Do this one rollout as stop-all-then-start, not rolling. An old binary and a new one would each run the background jobs for the length of a rolling deploy. Steps: Upgrading.
  • Production now refuses to start without REDIS_URL and TRUST_PROXY. Set both before you deploy. See Self-host.
  • Run migrations first, as for any upgrade.
  • Dashboards keyed on the HTTP metrics' path label now see route patterns such as /v1/customers/{id} instead of raw paths.
  • Set SMTP_FROM to an address on your own domain if you send email. The built-in default changed to billing@velox.local, a placeholder.

What changed

Invoices and credits

  • A billing tick bills every subscription that is due, not only the first 50.
  • A usage threshold no longer restarts graduated or package tiers for the rest of the period.
  • A credit note issued after customer credits were applied no longer relieves an invoice twice.
  • A cancel, downgrade or plan change records the customer's refund for the unused period in the same step, so it cannot be lost.
  • Credit-note numbers have no gaps.
  • "Due on receipt" (Net 0) applies to every invoice.
  • Voiding an invoice reverses all of its tax.

Payments and collection

  • A customer paying on the hosted invoice page and a Velox charge can no longer both take money for one invoice.
  • A charge's late result can no longer undo a payment or a decline recorded while it was in flight.
  • An invoice finalized just before a crash is collected on the next tick.
  • Dunning retries keep their spacing, and an interrupted final attempt is finished.
  • A card used on the hosted invoice page is saved to the customer.

Failing safely

  • Billing, tax, payments, plan changes and credit notes stop on a failed read instead of carrying on with a guessed value.

Running it

  • Two or more replicas behind a load balancer is the supported production setup. Background jobs run on leader leases, and a dead replica is replaced within seconds.
  • PgBouncer in transaction mode and RDS Proxy are supported.
  • Shutdown announces itself before it stops listening and fits a 90-second budget.
  • Go 1.26.9.

Dashboard

  • Sign-in over plain HTTP at an address other than localhost now says why it fails: it needs HTTPS or http://localhost.
  • "Report an issue" and the sign-in help link open GitHub Issues.

Housekeeping

  • v0.1.0 and v0.2.0 are retracted. They ship two commands that were later deleted and predate this release's billing fixes.
  • Nothing in Velox points at velox.dev any more. The project does not own that domain.

Docs

  • The self-host page and the install guide are rewritten in plain words.
  • A one-screen README, a one-page list of engineering principles, and a sourced comparison with Lago, OpenMeter, Flexprice and Kill Bill.

Every change, with its reasoning: CHANGELOG.

v0.3.2

Choose a tag to compare

@sagarsuperuser sagarsuperuser released this 25 Aug 21:51
7f603cd

Same product code as v0.3.1. This release exists to validate and ship the CI supply-chain fix (#849): tag runs now complete the full chain — image publish → Trivy scan → SBOM attached to this release → Grype scan → Cosign signature. The v0.3.0 and v0.3.1 images on GHCR are unsigned (their tag runs died at the SBOM step before signing); operators should pin v0.3.2+.

v0.3.1

Choose a tag to compare

@sagarsuperuser sagarsuperuser released this 25 Aug 18:57
0763f18

Patch: the "Fewer dependencies" README heading now uses typographic quotes — pkg.go.dev's outline nav HTML-escapes straight ASCII quotes and was showing literal &quot; entities. No code changes.