diff --git a/NFS-Server.md b/NFS-Server.md
index a2efc09..1d1fafe 100644
--- a/NFS-Server.md
+++ b/NFS-Server.md
@@ -150,6 +150,48 @@ servers are not reachable from the NFS host.
## Architecture
+```mermaid
+flowchart LR
+ subgraph Clients["NFS clients"]
+ LinuxClient["Linux kernel
nfs.ko (v3)"]
+ MacClient["macOS kernel
nfsv3"]
+ end
+
+ subgraph NFSHead["weed nfs (NFSv3 head)"]
+ direction TB
+ RPC["NFSv3 / MOUNT RPC
go-nfs backend"]
+ Handles["Deterministic filehandles
exportID | kind | inode | gen"]
+ MetaSub["Metadata subscription
(SubscribeMetadata)"]
+ Cache["Shared chunk reader cache"]
+ ACL["Client allowlist
(CIDR / host)"]
+ RPC --> Handles
+ RPC --> Cache
+ MetaSub -. invalidate .-> Cache
+ end
+
+ subgraph Cluster["SeaweedFS cluster"]
+ direction TB
+ Filer["Filer
metadata + AssignVolume"]
+ InodeIdx[("Inode secondary index
inode -> paths, generation")]
+ Volumes["Volume servers
chunk read / write"]
+ Filer --- InodeIdx
+ Filer -. AssignVolume .-> Volumes
+ end
+
+ LinuxClient -- "port=2049
mountport=2049" --> RPC
+ MacClient -- "port=2049
mountport=2049" --> RPC
+ ACL -. gate .-> RPC
+
+ Handles -- "lookup / readdir / create
rename / remove / link" --> Filer
+ Handles -- "resolve inode -> path" --> InodeIdx
+ Filer -- "change events" --> MetaSub
+
+ RPC -- "chunk read / write
(direct | publicUrl | filerProxy)" --> Volumes
+ RPC -. "inline <= 4 MiB fast path
UpdateEntry only" .-> Filer
+```
+
+Request flow in one line: NFS RPC arrives at the head, the filehandle decodes into an inode, the filer resolves the inode through its secondary index for metadata work, and chunk bytes flow straight between the NFS head and the volume servers. The filer's `SubscribeMetadata` stream keeps the head's chunk cache coherent with other writers.
+
### Deterministic Filehandles
Every filer entry in this PR now carries a filer-assigned inode