Packages

Elixir NIF bindings for the Lenny secret redaction engine

Current section

Files

Jump to
lenny_nif lib lenny.ex
Raw

lib/lenny.ex

defmodule Lenny do
@moduledoc """
Secret redaction engine for Elixir/Erlang.
Uses BLAKE3 + rolling hash for high-performance scanning
with zero false positives and no secrets stored in memory.
## Example
engine = Lenny.new_engine()
:ok = Lenny.load_secrets(engine, [%{name: "db_pass", value: "hunter2", tier: "alert"}])
result = Lenny.scan_string(engine, "password is hunter2")
# result.output == "password is [REDACTED:db_pass]"
# result.has_redactions == true
"""
use Rustler,
otp_app: :lenny_nif,
crate: "lenny_nif",
skip_compilation?: true,
load_from: {:lenny_nif, "priv/native/liblenny_nif"}
@doc "Create a new scanning engine with built-in patterns enabled."
def new_engine(), do: :erlang.nif_error(:not_loaded)
@doc "Create a new scanning engine with built-in patterns disabled."
def new_engine_no_patterns(), do: :erlang.nif_error(:not_loaded)
@doc """
Load secrets into the engine.
Each secret can be a map or a legacy 2-tuple.
Map format (preferred):
%{
name: "db_pass",
value: "hunter2",
tier: "alert", # "log" | "alert" | "page" — default: "log"
canary: false, # default: false
redaction: "tagged", # "tagged" | "full" | "partial" — default: "tagged"
prefix: 4, # chars to keep before redaction (only for "partial")
suffix: 4, # chars to keep after redaction (only for "partial")
transformations: [] # ["base64", "url"] — default: []
}
Legacy tuple format:
{"db_pass", "hunter2"}
"""
def load_secrets(_engine, _secrets), do: :erlang.nif_error(:not_loaded)
@doc """
Scan binary input. Returns a map:
%{
output: binary(),
has_redactions: boolean(),
has_canary_hit: boolean(),
has_exact_match_redactions: boolean(),
event_count: non_neg_integer(),
events: [event()]
}
Runs on a dirty CPU scheduler to avoid blocking the BEAM scheduler during
long-running scans.
"""
def scan(_engine, _input), do: :erlang.nif_error(:not_loaded)
@doc """
Scan string input. Returns the same map as `scan/2` but `output` is a string.
Runs on a dirty CPU scheduler to avoid blocking the BEAM scheduler during
long-running scans.
"""
def scan_string(_engine, _input), do: :erlang.nif_error(:not_loaded)
@doc "Create a pattern scanner with built-in gitleaks-compatible rules."
def new_pattern_scanner(), do: :erlang.nif_error(:not_loaded)
@doc """
Scan for pattern matches. Returns a list of maps:
[%{rule_id: String.t(), description: String.t(), tier: String.t(), start: non_neg_integer(), end: non_neg_integer()}]
Runs on a dirty CPU scheduler to avoid blocking the BEAM scheduler during
long-running scans.
"""
def scan_patterns(_scanner, _input), do: :erlang.nif_error(:not_loaded)
end