stable

nfs

File store for a pre-mounted NFS path, with pre-flight mount diagnostics for nolock / noac options that silently break versioning guarantees.

@noy-db/to-nfs · pnpm add @noy-db/to-nfs Same layout as to-file, plus NFS mount diagnostics.

Shaperecord
Atomic CAS✗
Localityremote
Speedslow
Persistencedurable

What it does

Uses the same JSON-file indexed layout as file for a pre-mounted NFS path, with pre-flight checks that flag mount options known to silently break versioning guarantees on stock NFS mounts: nolock (POSIX locks disabled → silent concurrent-write corruption), stale attribute caching (noac), and wrong filesystem type.

Notes

  • casAtomic: false — no CAS primitive; the pre-flight checks exist precisely because concurrent writers on a misconfigured mount can corrupt data silently.
  • Record shape, one file per record. Exposes a diagnostics() method returning { risks, info } for the mount.
  • Factory: toNfs(options: NfsStoreOptions).

The mount is cross-checked against the address

From 0.6.0-pre.1 the store compares the server:/export it was told about against the device string of the mount it actually opened — for an NFS mount the device is server:/export, and the detector was already parsing it out of /proc/mounts and discarding it. A pod claiming nas.local:/exports/vaults resolved against a mountPath pointing somewhere else no longer writes to the wrong export silently: diagnostics() adds a risk on mismatch.

onDeviceMismatch: 'warn' | 'error' on both NfsStoreOptions and NfsDescriptorOptions picks what happens, and defaults to 'warn' — an existing consumer whose descriptor is merely imprecise must not break on upgrade. Set 'error' when the address is meant to be authoritative.

The check is gated on both address halves and a device from the detector, so a detector that cannot report one (every pre-0.6.0-pre.1 implementation, including an injected test double) never manufactures a mismatch. Only trailing slashes are normalized; host case is left alone. This changes diagnostics() output — assert against risks by predicate, not by position.

Store-locator descriptor

See store-locator descriptors for the general model. kind: 'nfs', class: 'lan'. address is { server?, export? }, the logical server:/export identity. It still does not select the mount — binding.mountPath is what the store actually opens, and is required, since where an export is mounted is device-local and never travels in a descriptor — but from 0.6.0-pre.1 the factory forwards both halves and the store cross-checks them against the real mount. options is { onNolock?, onDeviceMismatch? }. binding.mountDetector? carries the existing test-injection seam through unchanged.

import { nfsStoreDescriptor, registerNfsStore } from '@noy-db/to-nfs'

const descriptor = nfsStoreDescriptor({ server: 'nas.local', export: '/vaults' })
// later: locator.resolve(descriptor, { binding: { mountPath: '/mnt/nas' } })

See also