I’ve just put together a small POC exploring PDF inspection from Elixir/Phoenix:
The idea is pretty simple: drag & drop a PDF in a Phoenix LiveView application, pass it to pdf2md from Firecrawl’s pdf-inspector, and display the extracted information (PDF type, number of pages, tables, Markdown, etc.).
The interesting part for me was not really the PDF processing itself, but how to integrate the Rust component with Elixir.
For this POC, I deliberately avoided a NIF.
Instead, the Rust binary is invoked through an OS Port:
Port.open(
{:spawn_executable, path},
[:binary, :exit_status, args: args]
)
The Rust binary is vendored in priv/rust_bin/bin/, so the Phoenix application can invoke it directly.
This feels like a rather nice boundary for this kind of workload:
-
Elixir/Phoenix remains responsible for the application and supervision.
-
Rust does the CPU-heavy/PDF-specific work.
-
No NIF means no risk of blocking or crashing the BEAM VM from native code.
-
The interface between the two sides remains very explicit.
Obviously, there are trade-offs compared with a NIF: process startup, serialization, IPC overhead, error handling, deployment, etc.
So I’m curious about how others in the Elixir ecosystem would approach this.
Would you use an OS Port for this kind of integration, or would you go for a NIF / Rustler?
This is only a POC for now, but I’m particularly interested in feedback on the architecture rather than the PDF extraction itself.























