Support policy

What Aurora Meter Pro promises about compatibility, and what it does not. It restates commitments the package already keeps; it adds no commercial term, it changes nothing in the licence, and it is not a service level agreement.

The surface it applies to is the API inventory. If an entry is not on that page, nothing here covers it. The free core carries a policy of its own for its own surface, on the same terms.

1. What SemVer covers

This package follows Semantic Versioning. Inside 1.x, these things do not break:

  • Every entry in the API inventory classed stable or optional-dep: its name, its arity, the shape of its arguments and the shape of what it returns.
  • The documented settlement semantics: a payment grants credit exactly once, keyed on the provider’s PaymentIntent id; a usage delta is billed at most once per (tenant, feature, period), through a reporting ledger that holds one row per window and sends it under an identifier the provider deduplicates on, so a retry after an answer that never arrived does not bill the window twice; and a refund or dispute reconciles to one net reversal capped at what the payment granted and at what earlier events already took.
  • Which money is in which unit: minor units (amount_cents) at every provider boundary, micro-dollar integers in the core ledger.
  • Configuration key names, their types, and their defaults.
  • Telemetry event names, measurement keys and metadata keys.
  • Migration entry points and the schema-version contract in section 3.
  • The set of provider webhook events the package requires you to subscribe to. Requiring a new one is a compatibility change and gets release notes that say so.

Additive change is allowed in a minor release: a new function, a new optional argument with a default, a new key in a returned map, a new metadata key on an existing telemetry event, a new configuration key whose default preserves today’s behaviour. Match on the keys you need rather than on a whole map, and additive change stays invisible to you.

2. What SemVer does not cover

  • Anything classed internal in the inventory, whether or not it is public in the compiled code. Section 11 of that page names every internal module.
  • Any function carrying @doc false, which includes every Oban.Worker, Plug, Phoenix.LiveView and provider-behaviour callback. Call the named entry point (report_all/0, not perform/1).
  • The rendered HTML and the CSS class names of the dashboard components.
  • The wording of log messages, and the exact text inside an exception message. The tuples are covered; the strings are not.
  • The SQL Pro generates and the query plans Postgres chooses for it.
  • The shape of a provider object. Stripe owns that, and section 5 says what this package does about it.
  • The five direct core-table reads recorded in section 12.1 of the inventory. They are coupling, they are scheduled for removal, and they are written down so that removing them surprises nobody.

3. Schema

A host owns its own database and runs its own migrations. Pro provides the migration bodies through AuroraMeter.Pro.Migration.up/1 and down/1, and the core provides its own: both must run, and Pro’s schema version 11 pairs with core schema version 10 in this release.

  • 1.x migrations are additive only. A new version adds tables, columns and indexes. A version that would destroy data is refused unless the caller passes confirm_data_loss: true, and no such version is planned for 1.x. A column that stops being written (the usage_reports.pending_* columns, for one) is deprecated in place and dropped no earlier than 2.0.
  • Every version is idempotent, so re-running one is safe.
  • Every supported 0.x history upgrades to 1.0. In practice the histories that exist are installs at Pro schema version 1 (from 0.1.x and 0.2.x) and at Pro schema version 9 (from 0.3.0); each has a path to the 1.0 schema through AuroraMeter.Pro.Migration.up(from: n), and each is exercised by the migration matrix in this repository’s CI on every push.
  • AuroraMeter.Pro.Migration.latest_version/0 reports the version the installed package expects.

4. Deprecations

A deprecated API keeps working. It warns, at most once per node per distinct key. It is not removed before 2.0.

Entry Replacement Removed in
AuroraMeter.Pro.Credits.Expirer AuroraMeter.Oban.CreditExpiry, in the free core 2.0
AuroraMeter.Pro.Credits.with_lock/2 under that name AuroraMeter.Pro.Lock.with_lock/2 2.0
AuroraMeter.Pro.Subscriptions.plan_id/1 plan_ref/1, returning {plan_id, version} 2.0
AuroraMeter.Pro.Config.plan_for_price/1 plan_ref/1, returning {plan_id, version} 2.0
A bare plan_id key in :stripe_prices {plan_id, version} 2.0
usage_reports.pending_* columns the outbox item’s own state 2.0

Each of those replacements arrives with 1.0. Until it does, the entry on the left is simply the API, and this table is the advance notice. AuroraMeter.Pro.Credits.Expirer is the first whose replacement has landed: it is now a shim that delegates to the core worker, its run/0 carries @deprecated, and the scheduler map is the one-line change.

5. Working against a provider

Aurora Meter Pro talks to Stripe, and Stripe’s contract is not ours to promise. Three consequences, stated plainly:

  • A provider object’s shape is Stripe’s. Pro reads the fields it names and ignores the rest. A field Stripe removes is a Stripe change; Pro adapts to it in a patch release and says so in the changelog.
  • Provider API versions are recorded, not reconciled. Webhook payloads arrive serialised at the account’s default API version, while the package’s own requests go out at the client library’s default. Those can differ, and where evidence exists it records both rather than reporting one number.
  • Pro does not claim automatic resolution of every unknown provider outcome. An uncertain result is held, retried against the provider’s current state, and surfaced, and the recovery guide says what an operator does with it.

Cancelled refunds

Cancelled refunds. A refund that ends in canceled is handled identically to one that ends in failed. This is true by construction: Aurora Meter Pro never reads a refund’s status. It reconciles from the charge’s current amount_refunded and the charge’s current disputes, both re-read from Stripe at the moment the event is applied (Credits.retrieve_refund/4). The two statuses therefore cannot take different paths, and AuroraMeter.Pro.Credits.SyncRefundTest asserts the same outcome for both, including for a refund whose status contradicts the provider state.

The failed lifecycle is verified against a real Stripe test-mode account: a real refund reaching failed, the charge’s amount_refunded falling back to zero, and the ledger restoring exactly the refunded amount once across four deliveries and a redelivery. The canceled lifecycle is supported but unverified against a real provider, because Stripe’s own documentation (https://docs.stripe.com/refunds and https://docs.stripe.com/testing, read 2026-09-14) provides no way to cancel a card refund other than the Stripe Dashboard, and this project does not prove behaviour with a mechanism Stripe has not documented. That is a statement about the evidence, not a narrowing of the commitment: canceled remains a supported refund outcome.

The full reconciliation guarantee, its prerequisites and its known limits are I13 in Correctness.

6. Runtime support

The supported pairs are the ones CI exercises on every push, at the exact patch versions listed in the README’s “Supported versions” table. Nothing is claimed for a pair that is not tested.

  • Floor: Elixir 1.15.8 on Erlang/OTP 26.2.5.21. The floor is OTP 26 rather than the core’s OTP 25, because the resolved Stripe HTTP stack pulls in a dependency whose own build refuses OTP 25 before any Pro code compiles. Elixir 1.15 supports OTP 24 to 26 and no further, so 1.15.8 with OTP 26 is the only pair available at the floor.
  • The core requirement is ~> 0.4, stated in mix.exs and in section 12 of the inventory.
  • Optional phoenix_live_view is ~> 1.0, and ~> 1.0 only.

Phoenix LiveView 1.0, when LiveView is present at all

Aurora Meter Pro 1.0 requires phoenix_live_view ~> 1.0 when the optional dependency is present. A host with no LiveView keeps everything except AuroraMeter.Pro.Components and the Pro LiveDashboard page: the Stripe provider, the outbox, reconciliation, recovery, credits, alerts and rollups are all data and behaviour with no view in them.

0.3.0 declared ~> 0.20 or ~> 1.0, and the 0.20 half was never true. Every template in this package is written in LiveView 1.0’s curly body interpolation, and 0.20 renders a {...} in an element body as literal text rather than as an interpolation, so a 0.20 host compiled cleanly and rendered {@title} at its own operators. Narrowing the requirement replaces a claim that never worked.

The route is the same as core’s, and core’s docs/support-policy.md has the detail: upgrade LiveView to 1.0, or drop the optional dependency. Both packages made the change together, so there is no pairing in which one of them claims 0.20.

Dropping a supported pair is a compatibility decision and needs release notes that say so before the release that drops it. Raising the floor inside 1.x happens only for a pair that is no longer available or no longer builds.

The floor is a compatibility guarantee, not a recommendation. Elixir 1.15.8 is the last release of its line and Erlang/OTP 26 is past its upstream maintenance window, so neither will receive further security patches.

7. Security

Report a suspected vulnerability privately, never through a public channel and never with a real key, customer identifier or provider object in the report. SECURITY.md has the disclosure routes, the supported-version window for security fixes, how key material is handled and what the host is responsible for. It is not duplicated here, so that there is one description of the process and not two.

This package handles payment data only by reference: it stores a Stripe customer id, a payment method id and amounts. It never stores a card number, and its telemetry and log lines carry neither request bodies nor payment identifiers.

8. Support

There is no response-time commitment. Support is best effort. Buying Aurora Meter Pro buys the software and its updates on the terms in LICENSE.commercial, which this page does not change and does not extend. There is no service level agreement, and this policy does not create one.

The most useful report names the Pro and core package versions, the Elixir and Erlang/OTP versions, the Pro and core schema versions (AuroraMeter.Pro.Migration.latest_version/0 and AuroraMeter.Migration.latest_version/0), and the smallest configuration that reproduces the behaviour. Never include a live key or a customer identifier.

9. Releases and transition releases

Releases are published by the maintainer. There is no automatic publish from CI, and no agent publishes a package.

A change that moves a default, or that starts refusing something previously accepted, ships in two steps. First a transition release that keeps today’s behaviour, warns about what is going to change, and lets a host opt in early by setting the key explicitly. Then the release that moves the default, whose notes list every default that moved and the key that pins the old one.

Every release carries a CHANGELOG.md entry. An entry that changes behaviour names what a caller has to do, or says plainly that there is nothing to do.

Test helpers

AuroraMeter.Pro.Stripe.Fake and AuroraMeter.Pro.Credits.StripeClient.Fake ship in the package and are listed in the inventory as stable, because a host test suite that uses them should not break on a minor upgrade. They are governed here rather than by SemVer on every helper name: a fake may gain a control or a clearer failure message in a minor release, and a fake that becomes wrong (because the provider behaviour it simulates changed) is corrected rather than kept bug-compatible. A fake whose answers stopped matching the provider is worse than no fake at all.

Nothing in production should call them.