Current section
Files
Jump to
Current section
Files
README.md
# lenny_nif
Elixir NIF bindings for [Lenny](https://github.com/ZenSRE/lenny), a secret redaction engine built for AI coding tools.
Lenny scans output streams and redacts known secrets using BLAKE3 + rolling hash -- no secret values are stored in memory at runtime. It also includes 216 built-in pattern rules for detecting unknown secrets (API keys, tokens, connection strings).
## Installation
Add `lenny_nif` to your dependencies in `mix.exs`:
```elixir
def deps do
[{:lenny_nif, "~> 0.0.1"}]
end
```
### Requirements
- Elixir 1.17+
- Rust 1.88+ (installed via [rustup](https://rustup.rs/)) -- Rustler compiles the NIF from source
## Usage
### Exact-match redaction
Load known secrets, then scan text to redact any occurrences:
```elixir
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
```
### Pattern scanning
Detect secrets by pattern without loading known values:
```elixir
scanner = Lenny.new_pattern_scanner()
matches = Lenny.scan_patterns(scanner, "AWS_KEY=AKIAIOSFODNN7EXAMPLE")
hd(matches).rule_id #=> "aws-access-key"
```
## API Reference
### Engine
| Function | Description |
|---|---|
| `Lenny.new_engine/0` | Create engine with built-in pattern rules |
| `Lenny.new_engine_no_patterns/0` | Create engine for exact-match only |
| `Lenny.load_secrets/2` | Load secrets (list of maps or 2-tuples) |
| `Lenny.scan/2` | Scan binary input |
| `Lenny.scan_string/2` | Scan string input |
### Pattern Scanner
| Function | Description |
|---|---|
| `Lenny.new_pattern_scanner/0` | Create standalone pattern scanner |
| `Lenny.scan_patterns/2` | Returns list of match maps with `rule_id`, `description`, `tier`, `start`, `end` |
Scan functions run on dirty CPU schedulers to avoid blocking the BEAM.
### Secret format
Each secret is a map with required keys `name` and `value`:
```elixir
%{
name: "db_pass",
value: "hunter2",
tier: "alert", # "log" (default) | "alert" | "page"
canary: false, # default: false
redaction: "tagged", # "tagged" (default) | "full" | "partial"
prefix: 4, # chars to keep (partial only)
suffix: 4, # chars to keep (partial only)
transformations: [] # ["base64", "url"]
}
```
Legacy 2-tuple format is also supported: `{"db_pass", "hunter2"}`.
## Documentation
See the [Lenny project](https://github.com/ZenSRE/lenny) for configuration, threat model, and the complete list of pattern rules.
## License
MIT and Apache-2.0