Repository: kestra-io/kestra
Issue: Reserve the "system" namespace for kestra and raise a warning when using that namespace name + add a global setting for the name of that namespace
Labels: enhancement

### Feature description

We want to introduce system flows in 0.16 or 0.17 https://github.com/kestra-io/kestra/issues/3003 

To do that, we need to reserve the namespace name "system" for kestra 

For this release (0.15.0), we should show a warning in the UI when a user tries to create a flow with a system namespace saying: `"The system namespace is reserved for background workflows intended to perform routine tasks such as sending alerts and purging logs. Please use another namespace name."`

Issue comments:
There is a high risk that users already used `system` for their system flows.

What we can do however, is to use as _system_ namespace a namespace name that users cannot use as it's not valid (this may require some work on our side) like `*system*`. If those flows will not be editable from users the name of the namespace will not be that important so we may also generate it and register the generated name somewhere.
There is no risk, we won't delete any flows or namespaces for the user. The idea is that if they have a system namespace in 0.15.0, they will see a warning asking to change the name because this namespace will be reserved for system flows. Even if they have some of them, they will not be gone, they will just be hidden behind system flow so they will need to then move if they don't want to always click on the dropdown to find those flows and executions

I think this issue will clarify https://github.com/kestra-io/kestra/issues/3003 

For now, we only need a warning, that's all
There is no way to 'move' a flow so they will need to delete the flow and re-create it.
yes you're right, that's a tradeoff we have to make
The idea of hiding an _internal functionality_ performed by _utility flows_ is interesting. Could the name be configurable like the [env-vars-prefix](https://kestra.io/docs/administrator-guide/configuration/others#kestravariablesenv-vars-prefix) is?
> The idea of hiding an _internal functionality_ performed by _utility flows_ is interesting. Could the name be configurable like the [env-vars-prefix](https://kestra.io/docs/administrator-guide/configuration/others#kestravariablesenv-vars-prefix) is?

@yuri1969 we would like to call the feature system flows so the preference would be to put it in the system namespace for maximum clarity. Do you have some specific use cases that would require putting system flows in another namespace (i.e. making it a configuration)?
@anna-geller No particular use case here beyond flexibility.
ok, let's do it:

```yaml
kestra:
  system-flows:
    namespace: system
```

Pull request review comments:
I supposed that the syntax `:system` is used to set a default, but I think for user it would be great to put it in the application.yml, so its more explicit
Yes, it provides a default, we use this syntax already in a few places and it avoids adding it to the various configuration files we have.

>  for user it would be great to put it in the application.yml, so its more explicit

What we really need is an example config file with all configuration point and comments. I already thought about it. But this is for a different PR ;) 

Patch that resolved the issue:
diff --git a/cli/src/main/resources/application.yml b/cli/src/main/resources/application.yml
index c3bc4bf8fa..4bd0ebd6cf 100644
--- a/cli/src/main/resources/application.yml
+++ b/cli/src/main/resources/application.yml
@@ -214,4 +214,4 @@ kestra:
 
   heartbeat:
     frequency: 10s
-    heartbeat-missed: 3
+    heartbeat-missed: 3
\ No newline at end of file
diff --git a/core/src/main/java/io/kestra/core/models/validations/ValidateConstraintViolation.java b/core/src/main/java/io/kestra/core/models/validations/ValidateConstraintViolation.java
index f95482d39d..bc8891b676 100644
--- a/core/src/main/java/io/kestra/core/models/validations/ValidateConstraintViolation.java
+++ b/core/src/main/java/io/kestra/core/models/validations/ValidateConstraintViolation.java
@@ -33,6 +33,7 @@ public class ValidateConstraintViolation {
     private String constraints;
     private boolean outdated;
     private List<String> deprecationPaths;
+    private List<String> warnings;
 
     @JsonIgnore
     public String getIdentity(){
diff --git a/core/src/main/java/io/kestra/core/services/FlowService.java b/core/src/main/java/io/kestra/core/services/FlowService.java
index ab0cdc0ea9..8779145142 100644
--- a/core/src/main/java/io/kestra/core/services/FlowService.java
+++ b/core/src/main/java/io/kestra/core/services/FlowService.java
@@ -12,6 +12,7 @@
 import io.kestra.core.serializers.YamlFlowParser;
 import io.kestra.core.utils.ListUtils;
 import io.micronaut.context.ApplicationContext;
+import io.micronaut.context.annotation.Value;
 import io.micronaut.core.annotation.Nullable;
 import jakarta.inject.Inject;
 import jakarta.inject.Singleton;
@@ -53,6 +54,9 @@ public class FlowService {
     @Inject
     ApplicationContext applicationContext;
 
+    @Value("${kestra.system-flows.namespace:system}")
+    private String systemFlowNamespace;
+
     public FlowWithSource importFlow(String tenantId, String source) {
         Flow withTenant = yamlFlowParser.parse(source, Flow.class).toBuilder()
             .tenantId(tenantId)
@@ -76,6 +80,13 @@ public List<String> deprecationPaths(Flow flow) {
         return deprecationTraversal("", flow).toList();
     }
 
+    public List<String> warnings(Flow flow) {
+        if (flow != null && flow.getNamespace() != null && flow.getNamespace().equals(systemFlowNamespace)) {
+            return List.of("The system namespace is reserved for background workflows intended to perform routine tasks such as sending alerts and purging logs. Please use another namespace name.");
+        }
+        return Collections.emptyList();
+    }
+
     private Stream<String> deprecationTraversal(String prefix, Object object) {
         if (object == null || ClassUtils.isPrimitiveOrWrapper(object.getClass()) || String.class.equals(object.getClass())) {
             return Stream.empty();
diff --git a/ui/src/components/flows/ValidationError.vue b/ui/src/components/flows/ValidationError.vue
index 1042cec7ec..31b1970fd2 100644
--- a/ui/src/components/flows/ValidationError.vue
+++ b/ui/src/components/flows/ValidationError.vue
@@ -185,6 +185,7 @@
             background-color: white;
             white-space: pre;
             border-top: 1px solid var(--bs-gray-300);
+            text-wrap: initial;
 
             html.dark & {
                 color: white;
diff --git a/ui/src/components/inputs/EditorView.vue b/ui/src/components/inputs/EditorView.vue
index a8edfb0641..689a8dc057 100644
--- a/ui/src/components/inputs/EditorView.vue
+++ b/ui/src/components/inputs/EditorView.vue
@@ -127,9 +127,12 @@
             ?.map(f => `${f} ${t("is deprecated")}.`)
             ?? [];
 
+        const otherWarnings = props.flowValidation?.warnings ?? [];
+
         const warnings = [
             ...outdatedWarning,
-            ...deprecationWarnings
+            ...deprecationWarnings,
+            ...otherWarnings
         ];
 
         return warnings.length === 0 ? undefined : warnings;
diff --git a/webserver/src/main/java/io/kestra/webserver/controllers/FlowController.java b/webserver/src/main/java/io/kestra/webserver/controllers/FlowController.java
index 4e09da364a..4819966924 100644
--- a/webserver/src/main/java/io/kestra/webserver/controllers/FlowController.java
+++ b/webserver/src/main/java/io/kestra/webserver/controllers/FlowController.java
@@ -508,7 +508,7 @@ public List<ValidateConstraintViolation> validateFlows(
                     }
 
                     validateConstraintViolationBuilder.deprecationPaths(flowService.deprecationPaths(flowParse));
-
+                    validateConstraintViolationBuilder.warnings(flowService.warnings(flowParse));
                     validateConstraintViolationBuilder.flow(flowParse.getId());
                     validateConstraintViolationBuilder.namespace(flowParse.getNamespace());
 


Test patch:
diff --git a/core/src/test/java/io/kestra/core/services/FlowServiceTest.java b/core/src/test/java/io/kestra/core/services/FlowServiceTest.java
index b653cd40a5..9c00253189 100644
--- a/core/src/test/java/io/kestra/core/services/FlowServiceTest.java
+++ b/core/src/test/java/io/kestra/core/services/FlowServiceTest.java
@@ -1,6 +1,5 @@
 package io.kestra.core.services;
 
-import io.kestra.core.repositories.FlowRepositoryInterface;
 import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
 import org.junit.jupiter.api.Test;
 import io.kestra.core.models.flows.Flow;
@@ -13,12 +12,10 @@
 import jakarta.inject.Inject;
 
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.*;
 
 @MicronautTest
 class FlowServiceTest {
-    @Inject
-    private FlowRepositoryInterface flowRepository;
     @Inject
     private FlowService flowService;
 
@@ -41,7 +38,7 @@ private static Flow create(String tenantId, String flowId, String taskId, Intege
     }
 
     @Test
-    public void sameRevisionWithDeletedOrdered() {
+    void sameRevisionWithDeletedOrdered() {
         Stream<Flow> stream = Stream.of(
             create("test", "test", 1),
             create("test", "test2", 2),
@@ -57,7 +54,7 @@ public void sameRevisionWithDeletedOrdered() {
     }
 
     @Test
-    public void sameRevisionWithDeletedSameRevision() {
+    void sameRevisionWithDeletedSameRevision() {
         Stream<Flow> stream = Stream.of(
             create("test2", "test2", 1),
             create("test", "test", 1),
@@ -74,7 +71,7 @@ public void sameRevisionWithDeletedSameRevision() {
     }
 
     @Test
-    public void sameRevisionWithDeletedUnordered() {
+    void sameRevisionWithDeletedUnordered() {
         Stream<Flow> stream = Stream.of(
             create("test", "test", 1),
             create("test", "test2", 2),
@@ -90,7 +87,7 @@ public void sameRevisionWithDeletedUnordered() {
     }
 
     @Test
-    public void multipleFlow() {
+    void multipleFlow() {
         Stream<Flow> stream = Stream.of(
             create("test", "test", 2),
             create("test", "test2", 1),
@@ -108,4 +105,14 @@ public void multipleFlow() {
         assertThat(collect.stream().filter(flow -> flow.getId().equals("test2")).findFirst().orElseThrow().getRevision(), is(3));
         assertThat(collect.stream().filter(flow -> flow.getId().equals("test3")).findFirst().orElseThrow().getRevision(), is(3));
     }
+
+    @Test
+    void warnings() {
+        Flow flow = create("test", "test", 1).toBuilder().namespace("system").build();
+
+        List<String> warnings = flowService.warnings(flow);
+
+        assertThat(warnings.size(), is(1));
+        assertThat(warnings.get(0), containsString("The system namespace is reserved for background workflows"));
+    }
 }
\ No newline at end of file
diff --git a/webserver/src/test/java/io/kestra/webserver/controllers/FlowControllerTest.java b/webserver/src/test/java/io/kestra/webserver/controllers/FlowControllerTest.java
index b1bdedc3e1..e8355a9e39 100644
--- a/webserver/src/test/java/io/kestra/webserver/controllers/FlowControllerTest.java
+++ b/webserver/src/test/java/io/kestra/webserver/controllers/FlowControllerTest.java
@@ -715,8 +715,11 @@ void validateFlows() throws IOException {
         assertThat(body.get(0).isOutdated(), is(true));
         assertThat(body.get(0).getDeprecationPaths(), hasSize(3));
         assertThat(body.get(0).getDeprecationPaths(), containsInAnyOrder("tasks[1]", "tasks[1].additionalProperty", "listeners"));
+        assertThat(body.get(0).getWarnings().size(), is(1));
+        assertThat(body.get(0).getWarnings().get(0), containsString("The system namespace is reserved for background workflows"));
         assertThat(body.get(1).isOutdated(), is(false));
         assertThat(body.get(1).getDeprecationPaths(), empty());
+        assertThat(body.get(1).getWarnings(), empty());
         assertThat(body, everyItem(
             Matchers.hasProperty("constraints", is(nullValue()))
         ));
diff --git a/webserver/src/test/resources/application-test.yml b/webserver/src/test/resources/application-test.yml
index 39db962b2c..17381a0374 100644
--- a/webserver/src/test/resources/application-test.yml
+++ b/webserver/src/test/resources/application-test.yml
@@ -103,6 +103,7 @@ kestra:
       executionqueued:
         table: "execution_queued"
         cls: io.kestra.core.runners.ExecutionQueued
+
 datasources:
   h2:
     url: jdbc:h2:mem:public;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
diff --git a/webserver/src/test/resources/flows/validateMultipleValidFlows.yaml b/webserver/src/test/resources/flows/validateMultipleValidFlows.yaml
index 94533561f1..87c6239285 100644
--- a/webserver/src/test/resources/flows/validateMultipleValidFlows.yaml
+++ b/webserver/src/test/resources/flows/validateMultipleValidFlows.yaml
@@ -1,5 +1,5 @@
 id: "first_flow"
-namespace: "validation"
+namespace: "system"
 revision: 1
 tasks:
   - id: task_one