diff --git a/src/main/java/it/inaf/oats/vospace/DeleteNodeController.java b/src/main/java/it/inaf/oats/vospace/DeleteNodeController.java
index 6ca9f25a2392c39a5a1261f27a40098c987d8105..a77d23ae52b2dd35088623a6a983a11a1b520fb5 100644
--- a/src/main/java/it/inaf/oats/vospace/DeleteNodeController.java
+++ b/src/main/java/it/inaf/oats/vospace/DeleteNodeController.java
@@ -27,10 +27,10 @@ import org.springframework.web.bind.annotation.RestController;
 @RestController
 public class DeleteNodeController extends BaseNodeController  {
     
-    private static final Logger LOG = LoggerFactory.getLogger(DeleteNodeController.class);
-    
     @Autowired
-    private NodeDAO nodeDAO;
+    DeleteNodeService deleteNodeService;
+    
+    private static final Logger LOG = LoggerFactory.getLogger(DeleteNodeController.class);
 
     @DeleteMapping(value = {"/nodes", "/nodes/**"},
             produces = {MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE, MediaType.APPLICATION_JSON_VALUE})
@@ -38,49 +38,15 @@ public class DeleteNodeController extends BaseNodeController  {
         
         String path = getPath();
         LOG.debug("deleteNode called for path {}", path);
-        
-        // Check if the node is present, 
-        // if the node does not exist the service SHALL throw a HTTP 404 status code 
-        // including a NodeNotFound fault in the entity-body 
-        // If note present, got it
-        Node toBeDeletedNode = nodeDAO.listNode(path)
-                        .orElseThrow(() -> new NodeNotFoundException(path));               
-        
-        // If a parent node in the URI path is a LinkNode, the service SHALL throw 
-        // a HTTP 400 status code including a LinkFound fault in the entity-body.
-        // For example, given the URI path /a/b/c, the service must throw a HTTP 400 
-        // status code including a LinkFound fault in the entity-body if either /a 
-        // or /a/b are LinkNodes.
-        List<String> pathComponents = NodeUtils.subPathComponents(path);
-        if (pathComponents.isEmpty()) { 
-            
-            // Manage root node
-            throw PermissionDeniedException.forPath("/");
-            
-        } else {
-            
-            // Manage all precursors in full path
-            for (int i = 0; i < pathComponents.size(); i++) { 
-                String tmpPath = pathComponents.get(i);
-                Node mynode = nodeDAO.listNode(tmpPath)
-                        .orElseThrow(() -> new NodeNotFoundException(tmpPath));
-                if (mynode.getType().equals("vos:LinkNode") && i < pathComponents.size()-1) // a LinkNode leaf can be deleted
-                    throw new LinkFoundException(tmpPath);
-                            
-            }
-                    
-        }
-        
-        if (!NodeUtils.checkIfWritable(toBeDeletedNode, principal.getName(), principal.getGroups())) {
-            throw PermissionDeniedException.forPath(path);
-        }
                 
+        
         try {
-            nodeDAO.deleteNode(path);
+            deleteNodeService.doPreliminaryChecks(path);
+            deleteNodeService.deleteNode(path, principal);
             return ResponseEntity.ok("Node deleted");
         } catch(Exception ex) {
             return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
-        }
+        }       
         
     }
     
diff --git a/src/main/java/it/inaf/oats/vospace/DeleteNodeService.java b/src/main/java/it/inaf/oats/vospace/DeleteNodeService.java
new file mode 100644
index 0000000000000000000000000000000000000000..9366338934ea7154c68e7a2325882ddef5d77507
--- /dev/null
+++ b/src/main/java/it/inaf/oats/vospace/DeleteNodeService.java
@@ -0,0 +1,88 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package it.inaf.oats.vospace;
+
+import it.inaf.oats.vospace.datamodel.NodeUtils;
+import it.inaf.oats.vospace.exception.LinkFoundException;
+import it.inaf.oats.vospace.exception.NodeNotFoundException;
+import it.inaf.oats.vospace.exception.PermissionDeniedException;
+import it.inaf.oats.vospace.persistence.NodeDAO;
+import java.util.List;
+import net.ivoa.xml.vospace.v2.Node;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+import org.springframework.transaction.annotation.Isolation;
+import it.inaf.ia2.aa.data.User;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ *
+ * @author Nicola Fulvio Calabria <nicola.calabria at inaf.it>
+ */
+
+@Service
+@EnableTransactionManagement
+public class DeleteNodeService {
+    
+    @Autowired
+    protected NodeDAO nodeDao;
+
+    @Value("${vospace-authority}")
+    protected String authority;
+    
+    @Transactional(rollbackFor = { Exception.class }, 
+            isolation = Isolation.REPEATABLE_READ)
+    public void deleteNode(String path, User principal) {       
+        
+        Node toBeDeletedNode = nodeDao.listNode(path)
+                .orElseThrow(() -> new NodeNotFoundException(path));        
+        
+        if (!NodeUtils.checkIfWritable(toBeDeletedNode, principal.getName(), principal.getGroups())) {
+            throw PermissionDeniedException.forPath(path);
+        }
+        
+        nodeDao.deleteNode(path);              
+
+    }
+    
+    public void doPreliminaryChecks(String path) throws Exception {
+        // Check if the node is present, 
+        // if the node does not exist the service SHALL throw a HTTP 404 status code 
+        // including a NodeNotFound fault in the entity-body 
+        // If note present, got it
+        nodeDao.listNode(path)
+                .orElseThrow(() -> new NodeNotFoundException(path));               
+        
+        // If a parent node in the URI path is a LinkNode, the service SHALL throw 
+        // a HTTP 400 status code including a LinkFound fault in the entity-body.
+        // For example, given the URI path /a/b/c, the service must throw a HTTP 400 
+        // status code including a LinkFound fault in the entity-body if either /a 
+        // or /a/b are LinkNodes.
+        List<String> pathComponents = NodeUtils.subPathComponents(path);
+        if (pathComponents.isEmpty()) { 
+            
+            // Manage root node
+            throw PermissionDeniedException.forPath("/");
+            
+        } else {
+            
+            // Manage all precursors in full path
+            for (int i = 0; i < pathComponents.size(); i++) { 
+                String tmpPath = pathComponents.get(i);
+                Node mynode = nodeDao.listNode(tmpPath)
+                        .orElseThrow(() -> new NodeNotFoundException(tmpPath));
+                if (mynode.getType().equals("vos:LinkNode") && i < pathComponents.size()-1) // a LinkNode leaf can be deleted
+                    throw new LinkFoundException(tmpPath);
+                            
+            }
+                    
+        }
+    }
+}
diff --git a/src/main/java/it/inaf/oats/vospace/ImmutableService.java b/src/main/java/it/inaf/oats/vospace/ImmutableService.java
new file mode 100644
index 0000000000000000000000000000000000000000..ec7eb0ada3d30c85d0691964c2d447d11d38e852
--- /dev/null
+++ b/src/main/java/it/inaf/oats/vospace/ImmutableService.java
@@ -0,0 +1,40 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package it.inaf.oats.vospace;
+
+import it.inaf.ia2.aa.data.User;
+import it.inaf.oats.vospace.persistence.NodeDAO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+import org.springframework.transaction.annotation.Isolation;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ *
+ * @author Nicola Fulvio Calabria <nicola.calabria at inaf.it>
+ */
+@Service
+@EnableTransactionManagement
+public class ImmutableService {
+
+    @Autowired
+    protected NodeDAO nodeDao;
+
+    @Value("${vospace-authority}")
+    protected String authority;
+
+    @Transactional(rollbackFor = {Exception.class}, isolation = Isolation.REPEATABLE_READ)
+    public void setBranchImmutable(String rootNodeURI, boolean setImmutable, User user) {
+        
+        String rootNodeVosPath = URIUtils.returnVosPathFromNodeURI(rootNodeURI, authority);
+        
+        // Check if branch is busy
+        
+    }
+
+}
diff --git a/src/main/java/it/inaf/oats/vospace/MoveService.java b/src/main/java/it/inaf/oats/vospace/MoveService.java
index 1d22cf604ce0ca19f4cf8ee233d8b7362c9c01f8..96222e1c905a1f28681790f9fcadf6489426fb04 100644
--- a/src/main/java/it/inaf/oats/vospace/MoveService.java
+++ b/src/main/java/it/inaf/oats/vospace/MoveService.java
@@ -68,6 +68,11 @@ public class MoveService extends AbstractNodeService {
                 throw new NodeBusyException(sourcePath);
             }
 
+            // TODO create immutable node exception flavor
+            if (nodeDao.isBranchImmutable(sourceId)) {
+                throw new InternalFaultException("Source branch contains immutable nodes");
+            }
+
             if (!nodeDao.isBranchWritable(sourceId, user.getName(), user.getGroups())) {
                 throw PermissionDeniedException.forPath(sourcePath);
             }           
@@ -84,6 +89,7 @@ public class MoveService extends AbstractNodeService {
                 if(snd.isPermissionDenied()) throw PermissionDeniedException.forPath(destinationPath);                                                
                 if(!snd.isWritable()) throw new InternalFaultException("Destination is not writable: "+ destinationPath);
                 if(!snd.isContainer()) throw new InternalFaultException("Existing destination is not a container: " + destinationPath);
+                if(!snd.isImmutable()) throw new InternalFaultException("Destination is immutable: " + destinationPath);
                 
                 destinationNodeLtreePath = snd.getDestinationNodeLtreePath();
                 
diff --git a/src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java b/src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java
index c15550dfe1aac47abb0054a3efbfb4eb18443146..ab6de8f74ada5c8d3a30a6ed68a5b167038f00a7 100644
--- a/src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java
+++ b/src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java
@@ -257,6 +257,7 @@ public class NodeDAO {
                 + "n.creator_id <> ?) AS is_permission_denied,\n"
                 + "n.type = 'container' AS is_container,\n"
                 + "n.job_id IS NOT NULL AS busy_state\n"
+                + "n.immutable AS is_immutable\n"
                 + "FROM node n \n"
                 + "LEFT JOIN location loc ON loc.location_id = n.location_id\n"
                 + "WHERE n.node_id = id_from_vos_path(?)\n";
@@ -286,8 +287,9 @@ public class NodeDAO {
             Boolean isBusy = rs.getBoolean("busy_state");
             Boolean isPermissionDenied = rs.getBoolean("is_permission_denied");
             Boolean isSticky = rs.getBoolean("is_sticky");
+            Boolean isImmutable = rs.getBoolean("is_immutable");
 
-            ShortNodeDescriptor result = new ShortNodeDescriptor(nodePath, isContainer, isWritable, isBusy, isPermissionDenied, isSticky);
+            ShortNodeDescriptor result = new ShortNodeDescriptor(nodePath, isContainer, isWritable, isBusy, isPermissionDenied, isSticky, isImmutable);
 
             return Optional.of(result);
         });
@@ -766,14 +768,16 @@ public class NodeDAO {
         private final boolean busy;
         private final boolean permissionDenied;
         private final boolean sticky;
+        private final boolean immutable;
 
-        public ShortNodeDescriptor(String nodeLtreePath, boolean container, boolean writable, boolean busy, boolean permissionDenied, boolean sticky) {
+        public ShortNodeDescriptor(String nodeLtreePath, boolean container, boolean writable, boolean busy, boolean permissionDenied, boolean sticky, boolean immutable) {
             this.nodeLtreePath = nodeLtreePath;
             this.container = container;
             this.writable = writable;
             this.busy = busy;
             this.permissionDenied = permissionDenied;
             this.sticky = sticky;
+            this.immutable = immutable;
         }
 
         public String getDestinationNodeLtreePath() {
@@ -784,6 +788,10 @@ public class NodeDAO {
             return container;
         }
 
+        public boolean isImmutable() {
+            return immutable;
+        }
+
         public boolean isWritable() {
             return writable;
         }