Single source of truth for optional-dependency availability predicates.
Sigra guards several runtime features behind optional dependencies. This
module centralises those availability checks so a maintainer can answer
"is this optional dep available?" for every guarded dep by reading one
module, and so mix sigra.doctor (Phase 138) has a stable, predictable
surface to query.
Scope — runtime call-site guards only
These predicates are for runtime call-site guards only — the
if/unless checks that decide whether to invoke an optional dep at
call time. Two categories of guard are explicitly out of scope and
remain literal Code.ensure_loaded?/1 calls:
Compile-time
defmodulewrappers (lib/sigra/workers/*.ex,lib/sigra/audit/forwarders/threadline.ex) run before this module may be compiled and therefore do not delegate here (D-04). Routing a compile-time guard through this SOT risks compile-ordering circularity; those wrappers stay literal.Dynamic module atoms (host schema variables, internal conditionally-compiled workers) are not named optional deps and are also out of scope.
The one known non-delegated runtime check is lib/sigra/application.ex:77
(Code.ensure_loaded?(Oban) boot-warning cond), left literal by
deliberate decision (Open Question 1 default — minimal phase).
Encryption posture — config-driven, not load-driven
encryption_active?/1 answers "is the host configured with a real vault
(not the plaintext stub)?" by mirroring the __sigra_encryption_mode__/0
config check in the internal verify_vault!/1 in Sigra.Application. It does not call
Code.ensure_loaded?(Cloak) — a load check would return true even when
the host app is still on the plaintext stub, which is a silent at-rest
encryption regression (ASVS V6, D-07).
Covered optional dependencies
| Predicate | Module | Optional dep |
|---|---|---|
oban_available?/0 | Oban | {:oban, ...} |
bcrypt_available?/0 | Bcrypt | {:bcrypt_elixir, ...} |
eqrcode_available?/0 | EQRCode | {:eqrcode, ...} |
threadline_available?/0 | Threadline | {:threadline, ...} |
assent_available?/0 | Assent | {:assent, ...} |
swoosh_available?/0 | Swoosh | {:swoosh, ...} |
joken_available?/0 | Joken | {:joken, ...} |
hammer_available?/0 | Hammer | {:hammer, ...} |
req_available?/0 | Req | {:req, ...} |
swoosh_available?/0 and req_available?/0 are present for SOT
completeness and Phase 138 consumption. In the current codebase, Swoosh's
only lib/ guard is in the out-of-scope test-helper testing.ex:98, and
Req is a transitive dep guarded only at the compound check in
enterprise_connections/validation.ex:91 (whose load-half is delegated by
plan 03). The absence of further delegation sites for these two predicates
is intentional, not a missing-delegation gap.
Summary
Functions
Returns true when Assent is available as a loaded module.
Returns true when Bcrypt is available as a loaded module.
Returns true when the host app's encryption module is configured with a
real vault (not the plaintext stub).
Returns true when EQRCode is available as a loaded module.
Returns true when Hammer is available as a loaded module.
Returns true when Joken is available as a loaded module.
Returns true when Oban is available as a loaded module.
Returns true when Req is available as a loaded module.
Returns true when Swoosh is available as a loaded module.
Returns true when Threadline is available as a loaded module.
Functions
@spec assent_available?() :: boolean()
Returns true when Assent is available as a loaded module.
Used to gate OAuth / OIDC strategy execution. When false, calling any
OAuth strategy raises with an actionable message asking the adopter to
add {:assent, "~> 0.3"} to mix.exs.
@spec bcrypt_available?() :: boolean()
Returns true when Bcrypt is available as a loaded module.
Used to gate the bcrypt transparent-migration path in
Sigra.Crypto and Sigra.Hashers.Bcrypt. When false, bcrypt
hash verification falls back to a constant-time no-op that prevents
timing side-channels.
Returns true when the host app's encryption module is configured with a
real vault (not the plaintext stub).
Derives the host's *.Encrypted.Binary module from the :user_schema
key in host_sigra (same derivation as the internal verify_vault!/1 in Sigra.Application
and encrypted_binary_module/1), then checks whether that module exports
__sigra_encryption_mode__/0 and returns a non-:stub value.
Returns false in any of these cases:
:user_schemais absent or not an atom.- The derived
*.Encrypted.Binarymodule does not export__sigra_encryption_mode__/0. __sigra_encryption_mode__/0returns:stub(plaintext passthrough active).
Does not call Code.ensure_loaded?(Cloak) — a load check would return
true even while the app is still on the plaintext stub, silently
misreporting encryption posture (D-07, ASVS V6).
@spec eqrcode_available?() :: boolean()
Returns true when EQRCode is available as a loaded module.
Used to gate QR code SVG generation in Sigra.MFA. When false,
generate_qr_svg/1 returns nil.
@spec hammer_available?() :: boolean()
Returns true when Hammer is available as a loaded module.
Used to gate rate-limiter resolution in Sigra.Plug.RateLimit. When
false, Sigra.RateLimiters.Noop is used as a fail-open fallback with
a logged warning.
@spec joken_available?() :: boolean()
Returns true when Joken is available as a loaded module.
Used to gate JWT signing/verification in Sigra.JWT.Signer. When
false, calling Joken-dependent functions raises with an actionable
message.
@spec oban_available?() :: boolean()
Returns true when Oban is available as a loaded module.
Used to gate Oban job-queue features (background email delivery, token
cleanup, audit forwarding). When false, Sigra falls back to inline
behaviour.
@spec req_available?() :: boolean()
Returns true when Req is available as a loaded module.
Included for SOT completeness and mix sigra.doctor consumption. Req
is a transitive dep; the runtime guard in
enterprise_connections/validation.ex:91 checks both availability and a
specific function export (function_exported?(Req, :get, 1)) — the
load-half delegates here, the function-export half stays at the call site
(D-06).
@spec swoosh_available?() :: boolean()
Returns true when Swoosh is available as a loaded module.
Included for SOT completeness and mix sigra.doctor consumption.
The only lib/ guard for Swoosh in the current codebase is the
test-helper Sigra.Testing module (out of runtime scope).
@spec threadline_available?() :: boolean()
Returns true when Threadline is available as a loaded module.
Used to gate the Threadline audit-forwarding path in
Sigra.Audit.Forwarders.Threadline. When false, the Noop forwarder
is used.