All notable changes to this project are documented here. The format follows Keep a Changelog, and this project adheres to Semantic Versioning.

[0.1.0] — 2026-08-08

First release.

Not yet verified against production

AshOpenFeed.revoke/2 and the success path of AshOpenFeed.refresh_status/2 require the openfeed-au:grant:self:query and openfeed-au:grant:self:revoke scopes, which the registration used for development was not granted. Both are covered by tests against a stub server, but neither has been exercised against the real API. Everything else here has.

reconcile_grants/2 has been run against production, including its degradation path when those scopes are absent.

Requires

  • Elixir 1.17+ and OTP 27+. The OTP floor comes from oidcc, which does not compile on OTP 26; both packages check and raise a clear error. Verified in CI across Elixir 1.17-1.20.

Added

  • AshOpenFeed.Grant — Ash resource extension. Injects the token attributes, the grant_id identity and the lifecycle actions into a resource you own. Uses Ash's add_new_* builders throughout, so your own declarations win.
  • AshOpenFeed — runtime API: authorize_url/3, complete_authorization/4, access_token/2, with_token/3, revoke/2, refresh_status/2.
  • AshOpenFeed.Info — introspection, plus config/2 resolving an OpenFeed.Config from application config or a provider.
  • AshOpenFeed.ConfigProvider — behaviour for credentials that cannot come from static config, such as a registration per tenant.
  • AshOpenFeed.KeyStore.Ash — signing key in an Ash resource, so every node signs with the same key.
  • mix ash_openfeed.install — Igniter installer. Generates the domain, grant and key resources, an editable consent controller, routes and config.
  • Compile-time verifiers: one rejecting a resource with no credential source, one warning when tokens have no visible encryption at rest.

Added

  • AshOpenFeed.amend_url/3 — send a consumer back to OpenFeed to change which accounts they share. OpenFeed keeps the same grant_id and bumps its revision, so the existing callback handles the return trip.
  • AshOpenFeed.reconcile_grants/2 — diff local grants against OpenFeed's app-level index. There are no webhooks, so this is the only way to notice a revocation or amendment without first failing a data call. Phase one needs only the app-level scope, so it works without grant-management scopes; phase two degrades gracefully, still recording the revision.
  • banking_account_ids / energy_account_ids attributes and a sync_grant_state action.

Fixed

  • A rejected refresh now marks the grant revoked. Previously with_token/3's with short-circuited on access_token/2, so a refresh failure never reached reconciliation and the grant stayed :active indefinitely.
  • refresh_status/2 read only grantStatus and meteringState, discarding the rest of the response. It now maps grantRevision and the authorised account id sets too — the injected revision attribute was previously never written.
  • complete_authorization/4 warns when grant_management? true but the scopes were not granted, rather than letting revoke/2 fail with a 403 at the moment a consumer is trying to disconnect.

Documentation

  • A Collecting data into your own resources section: the full sync loop, error collection, streaming large collections, and an AshOban schedule. Previously a single line.

Notes

  • The injected identity gets pre_check_with automatically on data layers that cannot enforce identities (ETS, Mnesia), since the identity is injected and could not otherwise be fixed by the user. Because a pre-checked identity installs a before_action hook, require_atomic? false is applied to the update actions in exactly that case — AshPostgres keeps atomic updates.