import { StoreDescriptor, StoreFactory, StoreLocator, NoydbStore } from '@noy-db/hub/to'; /** * **@noy-db/to-nfs** — NFS network-filesystem store for noy-db. * * NFS authentication is handled entirely outside noy-db (`AUTH_SYS` * UID/GID or Kerberos via `kinit`). This package wraps a self-contained * file-backed store with **NFS-specific pre-flight checks** — the kind * of thing that silently corrupts a store when ignored: * * 1. **`nolock` mount option** disables POSIX file locks. Silent * concurrent-write corruption follows. We parse `/proc/mounts` * (Linux) and throw (or warn) when the flag is active. * 2. **Attribute caching (`noac` absent)** — stale `mtime` / size * cached by the client can let a version check pass on data * that's already advanced on the server. * 3. **Wrong filesystem type** — the mount may silently be ext4 * (e.g. pointing at the wrong path). We confirm `statfs.type` is * one of the NFS families. * * When running on non-Linux (macOS), `/proc/mounts` is absent so the * checks degrade to a warning. The store still functions — it simply * cannot self-diagnose the mount state. * * ## Not included * * - Native NFS client. noy-db works against a **pre-mounted** NFS * path; use `mount.nfs4` / `/etc/fstab` as usual. * - Kerberos ticket management. `kinit` is the user's responsibility. * The store surfaces `EKEYEXPIRED` as a clear error. * * @packageDocumentation */ interface NfsStoreOptions { /** Pre-mounted NFS directory. Fails fast if absent. */ readonly mountPath: string; /** * On detection of `nolock`, behavior is `'warn'` (default) or * `'error'`. Silent is not an option — NFS is tricky enough. */ readonly onNolock?: 'warn' | 'error'; /** Override the mount detector — injection seam for tests. */ readonly mountDetector?: MountDetector; /** * Logical NFS server this store believes it is talking to. Supply with * {@link NfsStoreOptions.export} to cross-check the claim against the * device the mount actually came from (#70). Both halves are required — * a half-stated identity is not checkable. */ readonly server?: string; /** Logical NFS export path. See {@link NfsStoreOptions.server}. */ readonly export?: string; /** * On a `server:/export` ↔ mounted-device mismatch, behavior is `'warn'` * (default) or `'error'`. Mirrors {@link NfsStoreOptions.onNolock}, and * defaults to warn for the same reason: an existing consumer whose * descriptor is merely imprecise must not break on upgrade. */ readonly onDeviceMismatch?: 'warn' | 'error'; } interface MountInfo { readonly exists: boolean; /** * The mount's device string — `server:/export` for an NFS mount, * `/dev/...` for a local one. `parts[0]` of the `/proc/mounts` line. * Absent when the detector cannot determine it. */ readonly device?: string; readonly fstype?: string; readonly options?: readonly string[]; } type MountDetector = (mountPath: string) => Promise; /** * Default mount detector — parses `/proc/mounts` on Linux. Returns * `{ exists: false }` on non-Linux or when the path isn't listed. */ declare function detectMount(mountPath: string): Promise; /** * Synchronous diagnostics run at store construction. Returns a list of * risk strings; empty = clean mount. Exposed so the consumer can log * or ship the report to an observability stack. */ declare function runMountDiagnostics(options: NfsStoreOptions): Promise<{ risks: string[]; info: MountInfo; }>; /** * Create an NFS-backed noy-db store. Mount diagnostics run once on * first use and are cached — failures raise (or warn) based on * `onNolock`. */ declare function toNfs(options: NfsStoreOptions): NoydbStore & { diagnostics(): Promise<{ risks: string[]; info: MountInfo; }>; }; /** * Serializable location of an NFS store. `server` and `export` describe * the logical `server:/export` identity. * * They do not OPEN anything — `binding.mountPath` is what the store opens, * because where an export is mounted is device-local and must never travel * in a pod. But since #70 they are no longer inert: when both are present * they are cross-checked against the device the mount actually came from, * which turns the identity from a decorative claim into a checked * invariant. See {@link NfsDescriptorOptions.onDeviceMismatch}. */ interface NfsAddress { /** Logical NFS server. Cross-checked against the mount device (#70). */ readonly server?: string; /** Logical NFS export path. Cross-checked against the mount device (#70). */ readonly export?: string; } /** Serializable tuning carried on the descriptor (never credentials). */ interface NfsDescriptorOptions { readonly onNolock?: 'warn' | 'error'; /** * Severity of an `address` ↔ mount-device mismatch: `'warn'` (default) * or `'error'`. See {@link NfsStoreOptions.onDeviceMismatch}. */ readonly onDeviceMismatch?: 'warn' | 'error'; } /** * Device-local supplement resolved at `resolve()` time — where the export * is actually mounted on this machine, which the descriptor cannot carry. * Never serialized into a pod alongside the descriptor. */ interface NfsBinding { readonly mountPath: string; readonly mountDetector?: MountDetector; } /** * Builds the `StoreDescriptor` form of a `toNfs()` store: `kind: 'nfs'`, * `class: 'lan'`, with the identity address and the serializable tuning as * `options`. Credentialless by construction — the live mount point arrives * via `binding.mountPath` at `resolve()` time. */ declare function nfsStoreDescriptor(address: NfsAddress, options?: NfsDescriptorOptions): StoreDescriptor; /** * `StoreFactory` for `to-nfs`: reconstructs the same store `toNfs()` * builds, from a descriptor produced by {@link nfsStoreDescriptor}. * `opts.binding.mountPath` is required — `toNfs()` fails fast without a * mount point, and where an export is mounted is device-local and never * travels in a descriptor. */ declare const nfsStoreFactory: StoreFactory; /** Registers {@link nfsStoreFactory} under the `'nfs'` kind on `locator`. */ declare function registerNfsStore(locator: StoreLocator): void; export { type MountDetector, type MountInfo, type NfsAddress, type NfsBinding, type NfsDescriptorOptions, type NfsStoreOptions, detectMount, nfsStoreDescriptor, nfsStoreFactory, registerNfsStore, runMountDiagnostics, toNfs };