Harper
Elixir bindings to Harper — a fast, offline, privacy-first English grammar and spell checker written in Rust by Automattic. Harper runs entirely in-process: no server, no network, no LLM, no model download. It's the engine behind writewithharper.com.
This package wraps harper-core as a Rustler
NIF and ships precompiled binaries for common targets, so most consumers
don't need a Rust toolchain.
Installation
def deps do
[{:harper, "~> 0.1"}]
end
Precompiled artifacts are provided for macOS (aarch64/x86_64) and Linux
(aarch64/x86_64, gnu). Other targets — including FreeBSD — build from source
via rustler, which needs a Rust toolchain (≥ 1.85, for edition 2024). To force
a source build on a supported target:
RUSTLER_PRECOMPILATION_FORCE_BUILD=1 mix deps.compile harper
Usage
Harper.lint/1,2,3 returns a list of Harper.Lint structs. It's a pure,
stateless function and runs on a dirty CPU scheduler, so linting a large
document won't block the BEAM's normal schedulers.
iex> Harper.lint("Ths is a testt.")
[
%Harper.Lint{
start: 0, end: 3, kind: "Spelling", priority: 63,
message: "Did you mean to spell `Ths` this way?",
suggestions: [%Harper.Suggestion{kind: "replace", text: "This"}, ...]
},
...
]
iex> Harper.lint("This is a clean sentence.")
[]
Dialects
English spelling is dialect-sensitive. lint/2 takes one of Harper.dialects/0
(:american — the default — :british, :canadian, :australian, :indian):
iex> Harper.lint("I love the colour blue.", :american) |> Enum.any?(& &1.kind == "Spelling")
true
iex> Harper.lint("I love the colour blue.", :british) |> Enum.any?(& &1.kind == "Spelling")
false
Custom dictionary
Pass words (proper nouns, brand terms, handles) that should never be flagged as misspellings — the server-side equivalent of "add to dictionary":
iex> Harper.lint("I write for acme daily.", :american, ["acme"]) |> Enum.any?(& &1.kind == "Spelling")
false
Offsets
Harper.Lint:start/:end are UTF-16 code-unit offsets into the linted
string, end-exclusive — chosen so they drop straight into JavaScript string
indexing and editor APIs (Quill, contenteditable ranges) without conversion.
Notes
- English only. Gate callers to English-authored content.
:kindis Harper'sLintKind("Spelling","Agreement","WordChoice","Style","Typo","Miscellaneous", …) — useful for grouping/colouring.Harper.Suggestion:kindis"replace","insert_after", or"remove"; apply it over the lint's span.
Releasing (maintainers)
Precompiled tarballs are built locally on macOS and hosted on the
sr.ht release ref. See scripts/:
build_artifacts.sh— mac (native) + linux (viacargo-zigbuild).build_freebsd_via_ssh.sh— FreeBSD, built on a remote box (opt-in).deploy.sh— bump, build, hash into the checksum manifest, tag, upload,mix hex.publish.
License
Apache-2.0. Bundles the Apache-2.0 harper-core (statically linked into the
NIF); each precompiled tarball ships LICENSE, LICENSE-APACHE, and a
THIRD-PARTY-NOTICES file generated from the full dependency tree.