Packages

ONC RPC and NFSv3 server as a standalone Elixir library.

Current section

Files

Jump to
tahr README.md
Raw

README.md

[![CI](https://harton.dev/james/tahr/actions/workflows/ci.yml/badge.svg)](https://harton.dev/james/tahr/actions)
[![Licence](https://img.shields.io/badge/licence-Apache--2.0-blue.svg)](LICENSE)
# Tahr
A standalone Elixir library for building NFSv3 file servers natively
on the BEAM — no kernel NFS server, no NIFs, no out-of-tree Rust. If
you can implement a behaviour, you can serve NFS from an Elixir
application.
The whole stack is here, from the wire up:
- `Tahr.XDR` — codec for every ONC RPC and NFSv3 data structure
- `Tahr.RPC` — the ONC RPC framework (record marking, programs,
TCP transport)
- `Tahr.Mount` — the MOUNT protocol, against a
`Tahr.Mount.Backend` behaviour
- `Tahr.NFSv3` — the NFSv3 procedures, against a
`Tahr.NFSv3.Backend` behaviour
Implement the two backend behaviours over your storage and any NFS
client built into Linux, macOS, or Windows can mount it.
The library follows the same split-of-concerns pattern as
[`firkin`](https://hex.pm/packages/firkin) (S3) and
[`davy`](https://hex.pm/packages/davy) (WebDAV): protocol library
here, storage-specific backend elsewhere.
## Installation
Add `tahr` to your dependencies:
```elixir
def deps do
[{:tahr, "~> 0.1.0"}]
end
```
## Quick start
Implement the two backend behaviours over your storage:
- `Tahr.Mount.Backend` — export resolution (which clients may
mount which paths)
- `Tahr.NFSv3.Backend` — filesystem operations (getattr, read,
write, readdir, and friends)
Then wire the protocol handlers and the RPC listener into your
supervision tree:
```elixir
programs = %{
100_005 => %{3 => Tahr.Mount.Handler.with_backend(MyApp.MountBackend)},
100_003 => %{3 => Tahr.NFSv3.Handler.with_backend(MyApp.NFSBackend)}
}
children = [
{Tahr.RPC.Server, port: 2049, programs: programs}
]
Supervisor.start_link(children, strategy: :one_for_one)
```
The portmapper (program 100000) is always registered so that NFS
clients can discover the server, and the NFSv3 mapping is announced
automatically on whatever port is bound.
## Features
- **Pure BEAM, from the wire up** — XDR (RFC 4506), ONC RPC v2 with
record marking and AUTH_SYS, MOUNT v3, and the full NFSv3 procedure
set (RFC 1813)
- **Pluggable storage** — two behaviours separate the protocol from
your storage layer
- **Streaming reads** — READ replies stream to the socket as iolists;
files are never materialised into a single binary
- **One process per connection** — slow clients don't affect anyone
else, and the listener drains in-flight RPCs on shutdown
- **Batteries included**`Tahr.InMemory`, a read-write reference
backend, and `mix tahr.serve` to mount it locally
## Try it locally
```bash
mix tahr.serve
# in another terminal
sudo mount -t nfs -o vers=3,tcp,port=2049,mountport=2049 \
127.0.0.1:/export /mnt/nfs
ls -l /mnt/nfs
```
## Tutorials
Step-by-step guides to building a backend, included in the
generated docs (`mix docs`):
- [Serving a read-only filesystem](documentation/tutorials/read-only-server.md)
- [A read-write server](documentation/tutorials/read-write-server.md)
## Building and testing
```bash
mix deps.get
mix compile
mix test
```
## Licence
[Apache-2.0](LICENSE)