0.10.0

New features

  • Discovery logging. Config-building helpers (ssl_opts/1, cowboy_opts/1, bandit_opts/1, phx_endpoint_config/1, etc.) now emit debug-level log messages describing discovered certificate groups, including hostnames, key type, validity dates, fingerprints, and chain validation status. Prefix(es) are included in the announcement line.

  • :log_level option. Pass log_level: :info (or any Logger level) to control the level of discovery log messages. Pass log_level: false or log_level: :none to suppress logging entirely. The default can also be set via config :snippy, log_level: :debug.

  • Change suppression. Repeated calls with the same certificate set produce no duplicate log output. After a Store.reload/1, logging is suppressed if the certificates are unchanged; only actual changes trigger re-emission.

0.9.0

Fixes

  • Helpers now work from config/runtime.exs before the Snippy application starts. Previously, calling Snippy.phx_endpoint_config/1, Snippy.ssl_opts/1, or any other helper before the supervision tree was running would crash with an ArgumentError on the missing :snippy_certs ETS table. Snippy now detects when its infrastructure is unavailable and transparently falls back to in-process discovery (scan + materialize without GenServer or ETS). No code changes required on the caller side.

New features

  • phx_endpoint_config/1 supports Bandit via :adapter option. Pass adapter: :bandit to nest SSL options under thousand_island_options: [transport_options: [...]] as Bandit requires. Defaults to :cowboy (flat merge, existing behavior).

Internals

  • Snippy.Store.lookup_groups/2 and Snippy.Store.discover/1 route through a local fallback path when the ETS table has not been created yet, reusing the existing Discovery.scan_all/1 and Discovery.materialize_group/2 pipeline.
  • Snippy.Store.current_scan/0 guards against a missing ETS table via :ets.whereis/1.
  • Removed a defensive {:error, :materialize_missing} branch in fetch_or_materialize/2; a missing row after a GenServer materialize call now crashes immediately (let-it-crash).
  • Snippy.TableOwner gained test-only __test_hide_table__/0 and __test_restore_table__/0 helpers for simulating a missing table without stopping the application.
  • 213 tests + 9 properties, 99.6% line coverage.

0.8.3

First release published to Hex.

Documentation-only fix: configure ex_doc to skip undefined-reference warnings on CHANGELOG.md so its narrative mentions of internal @moduledoc false modules (e.g. Snippy.Store) don't trigger build warnings. No code changes.

0.8.2 (unreleased)

Breaking changes

  • OCSP stapling support removed. The :ocsp_stapling? group flag, the _OCSP_STAPLING (and _OSCP_STAPLING typo-alias) suffixes, and the related extraction/parsing helpers are gone. OTP's :ssl does not perform server-side OCSP stapling, so the previous configuration knobs were inert. Strip any _OCSP_STAPLING env vars and any references to :ocsp_stapling? from your code.

New env-var aliases

  • _CERT and _CERT_FILE are now recognized alongside _CRT / _CRT_FILE. _CACERT / _CACERT_FILE are recognized alongside _CACRT / _CACRT_FILE. The two spellings are interchangeable for a given group.

Fixes & internals

  • Added an :errors field on %Snippy.Discovery{} listing per-group materialization failures as {prefix, key, reason} tuples (carried over from the 0.6.0 series and now stable).
  • Hardened the shared Snippy.Store: scan crashes and timeouts surface as structured errors rather than killing the GenServer; scheduled reload failures are logged but do not stop the timer.
  • Improved mix snippy.test reporting and added a --quiet flag.
  • Test suite cleanup: 208 tests + 9 properties, 99.6% line coverage, mix credo --strict clean, and a new mix lint alias chained into mix ci.

0.6.0 (breaking)

Breaking changes

All seven helper functions have been changed from (discovery, opts) to opts-only. Pass :prefix directly:

# 0.5.x
{:ok, disc} = Snippy.discover_certificates(prefix: "MYAPP")
Snippy.cowboy_opts(disc, port: 4443)

# 0.6.0
Snippy.cowboy_opts(prefix: "MYAPP", port: 4443)

The discovered_certs option lets advanced callers thread a pre-built %Snippy.Discovery{} through, recovering the old behavior and giving control over when materialization happens:

{:ok, disc} = Snippy.discover_certificates(prefix: "MYAPP")
Snippy.cowboy_opts(prefix: "MYAPP", discovered_certs: disc, port: 4443)

What changed under the hood

  • A single shared Snippy.Store GenServer now owns the env scan. The scan happens once on first use and is shared across every helper call, every prefix.
  • The scan no longer pre-filters by prefix. Helpers filter the in-memory scan list by prefix on every call, so adding new prefixes to a running system doesn't require a rescan.
  • PEM decoding, key decryption, validity checks, and chain validation happen lazily on first lookup of a (prefix, key) group. Both successes and failures are memoized in ETS.
    • Env vars whose (prefix, key) no helper ever asks about are never decoded, even if they look like Snippy vars.
    • This narrows the DoS surface against attackers who can set arbitrary env vars but don't control the running config.
  • The shared scan runs under a Task.Supervisor so a scan crash doesn't take the Store down.
  • %Snippy.Discovery{} gained an :errors field listing per-group materialization failures as {prefix, key, reason} tuples.
  • Dropped :memoize dependency; replaced with explicit ETS memoization.

Mix task

mix snippy.test now reports per-group materialization errors and prints a scope-survival summary when --only/--key is given. New --quiet flag suppresses per-group output.

Migration

Replace each call site:

BeforeAfter
Snippy.sni(disc, opts)Snippy.sni([prefix: ..., | opts])
Snippy.ssl_opts(disc, opts)Snippy.ssl_opts([prefix: ..., | opts])
Snippy.cowboy_opts(disc, opts)Snippy.cowboy_opts([prefix: ..., | opts])
Snippy.ranch_opts(disc, opts)Snippy.ranch_opts([prefix: ..., | opts])
Snippy.bandit_opts(disc, opts)Snippy.bandit_opts([prefix: ..., | opts])
Snippy.thousand_island_opts(disc, opts)Snippy.thousand_island_opts([prefix: ..., | opts])
Snippy.phx_endpoint_config(disc, opts)Snippy.phx_endpoint_config([prefix: ..., | opts])

If you want to keep using a pre-built handle, append discovered_certs: disc to each call.

0.5.0

Initial public release.