AppManager: Recursively monitor dodo resources

Change-Id: I23f3014e416f5b68f8909dbfbcba27db66eaee3f
diff --git a/core/installer/app_configs/app_base.cue b/core/installer/app_configs/app_base.cue
index 2b3ddd2..711dbe7 100644
--- a/core/installer/app_configs/app_base.cue
+++ b/core/installer/app_configs/app_base.cue
@@ -635,6 +635,19 @@
 	annotations: {...} | *{}
 	cluster?:         #Cluster | null
 	targetNamespace?: string
+	values: {...} | *{}
+
+	id: base64.Encode(null, sha256.Sum256(json.Marshal([
+		name,
+		values,
+		dependsOn,
+		if cluster != _|_ {
+			cluster
+		},
+		if targetNamespace != _|_ {
+			targetNamespace
+		},
+	])))
 	...
 }
 
@@ -707,12 +720,7 @@
 		helm: {
 			for name, r in out.helmR {
 				"\(name)": #HelmRelease & {
-					if r.id != _|_ {
-						_id: r.id
-					}
-					if r.id == _|_ {
-						_id: out.id
-					}
+					_id:           r.id
 					_name:         name
 					_chart:        _lc[r.chart.name]
 					_values:       r.values
@@ -748,15 +756,6 @@
 url: string | *""
 
 #WithOut: {
-	id: base64.Encode(null, sha256.Sum256(json.Marshal([
-		images,
-		charts,
-		helm,
-		openPort,
-		clusterProxy,
-	])))
-	_id: id
-
 	cluster?: #Cluster
 	if input.cluster != _|_ {
 		cluster: #Cluster | *input.cluster
@@ -866,7 +865,6 @@
 	helmR: {
 		for k, v in helm {
 			"\(k)": v & {
-				id: _id
 				if v.cluster == _|_ && _cluster != _|_ {
 					cluster: _cluster
 				}
diff --git a/core/installer/server/appmanager/server.go b/core/installer/server/appmanager/server.go
index 80f076a..e0aeab2 100644
--- a/core/installer/server/appmanager/server.go
+++ b/core/installer/server/appmanager/server.go
@@ -39,21 +39,20 @@
 }
 
 type Server struct {
-	l             sync.Locker
-	port          int
-	ssClient      soft.Client
-	repo          soft.RepoIO
-	m             *installer.AppManager
-	r             installer.AppRepository
-	fr            installer.AppRepository
-	reconciler    *tasks.FixedReconciler
-	h             status.ResourceMonitor
-	im            *status.InstanceMonitor
-	cnc           installer.ClusterNetworkConfigurator
-	vpnAPIClient  installer.VPNAPIClient
-	tasks         map[string]*taskForward
-	tmpl          tmplts
-	idToResources map[string]map[string][]status.Resource
+	l            sync.Locker
+	port         int
+	ssClient     soft.Client
+	repo         soft.RepoIO
+	m            *installer.AppManager
+	r            installer.AppRepository
+	fr           installer.AppRepository
+	reconciler   *tasks.FixedReconciler
+	h            status.ResourceMonitor
+	im           *status.InstanceMonitor
+	cnc          installer.ClusterNetworkConfigurator
+	vpnAPIClient installer.VPNAPIClient
+	tasks        map[string]*taskForward
+	tmpl         tmplts
 }
 
 type tmplts struct {
@@ -117,21 +116,20 @@
 		return nil, err
 	}
 	return &Server{
-		l:             &sync.Mutex{},
-		port:          port,
-		ssClient:      ssClient,
-		repo:          repo,
-		m:             m,
-		r:             r,
-		fr:            fr,
-		reconciler:    reconciler,
-		h:             h,
-		im:            im,
-		cnc:           cnc,
-		vpnAPIClient:  vpnAPIClient,
-		tasks:         make(map[string]*taskForward),
-		tmpl:          tmpl,
-		idToResources: make(map[string]map[string][]status.Resource),
+		l:            &sync.Mutex{},
+		port:         port,
+		ssClient:     ssClient,
+		repo:         repo,
+		m:            m,
+		r:            r,
+		fr:           fr,
+		reconciler:   reconciler,
+		h:            h,
+		im:           im,
+		cnc:          cnc,
+		vpnAPIClient: vpnAPIClient,
+		tasks:        make(map[string]*taskForward),
+		tmpl:         tmpl,
 	}, nil
 }
 
@@ -213,24 +211,12 @@
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 	} else {
-		var toMonitor []status.Resource
-		s.idToResources[instanceId] = map[string][]status.Resource{}
-		for _, r := range rr.Helm {
-			resource := status.Resource{
-				Type: status.ResourceHelmRelease,
-				ResourceRef: status.ResourceRef{
-					Name:      r.Name,
-					Namespace: r.Namespace,
-				},
-			}
-			toMonitor = append(toMonitor, resource)
-			if tmp, ok := s.idToResources[instanceId][r.Id]; ok {
-				s.idToResources[instanceId][r.Id] = append(tmp, resource)
-			} else {
-				s.idToResources[instanceId][r.Id] = []status.Resource{resource}
-			}
+		outs, err := status.DecodeResourceOuts(rr.RenderedRaw)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
 		}
-		s.im.Monitor(instanceId, toMonitor)
+		s.im.Monitor(instanceId, outs)
 		var cfg dodoAppRendered
 		if err := json.NewDecoder(bytes.NewReader(rr.RenderedRaw)).Decode(&cfg); err != nil {
 			http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -278,25 +264,14 @@
 	rr, err := s.m.Update(instanceId, nil, overrides)
 	if err != nil {
 		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
 	}
-	var toMonitor []status.Resource
-	s.idToResources[instanceId] = map[string][]status.Resource{}
-	for _, r := range rr.Helm {
-		resource := status.Resource{
-			Type: status.ResourceHelmRelease,
-			ResourceRef: status.ResourceRef{
-				Name:      r.Name,
-				Namespace: r.Namespace,
-			},
-		}
-		toMonitor = append(toMonitor, resource)
-		if tmp, ok := s.idToResources[instanceId][r.Id]; ok {
-			s.idToResources[instanceId][r.Id] = append(tmp, resource)
-		} else {
-			s.idToResources[instanceId][r.Id] = []status.Resource{resource}
-		}
+	outs, err := status.DecodeResourceOuts(rr.RenderedRaw)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
 	}
-	s.im.Monitor(instanceId, toMonitor)
+	s.im.Monitor(instanceId, outs)
 	t := tasks.NewInstallTask(s.h, func() (installer.ReleaseResources, error) {
 		if err == nil {
 			ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
@@ -767,68 +742,6 @@
 	return ret
 }
 
-type IdName struct {
-	Id   string
-	Name string
-}
-
-type IdNameMap map[string]IdName
-
-type resourceOuts struct {
-	Outs map[string]struct {
-		PostgreSQL IdNameMap `json:"postgresql"`
-		MongoDB    IdNameMap `json:"mongodb"`
-		Volume     IdNameMap `json:"volume"`
-		Ingress    IdNameMap `json:"ingress"`
-	} `json:"outs"`
-}
-
-type DodoResource struct {
-	Type string
-	Name string
-}
-
-type DodoResourceStatus struct {
-	Type   string `json:"type"`
-	Name   string `json:"name"`
-	Status string `json:"status"`
-}
-
-func orginize(raw []byte) (map[string]DodoResource, error) {
-	var outs resourceOuts
-	if err := json.NewDecoder(bytes.NewReader(raw)).Decode(&outs); err != nil {
-		return nil, err
-	}
-	ret := map[string]DodoResource{}
-	for _, out := range outs.Outs {
-		for _, r := range out.PostgreSQL {
-			ret[r.Id] = DodoResource{
-				Type: "postgresql",
-				Name: r.Name,
-			}
-		}
-		for _, r := range out.MongoDB {
-			ret[r.Id] = DodoResource{
-				Type: "mongodb",
-				Name: r.Name,
-			}
-		}
-		for _, r := range out.Volume {
-			ret[r.Id] = DodoResource{
-				Type: "volume",
-				Name: r.Name,
-			}
-		}
-		for _, r := range out.Ingress {
-			ret[r.Id] = DodoResource{
-				Type: "ingress",
-				Name: r.Name,
-			}
-		}
-	}
-	return ret, nil
-}
-
 func (s *Server) handleInstanceStatusAPI(w http.ResponseWriter, r *http.Request) {
 	s.l.Lock()
 	defer s.l.Unlock()
@@ -842,36 +755,15 @@
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 	}
-	idStatus := map[string]status.Status{}
-	for id, resources := range s.idToResources[instanceId] {
-		st := status.StatusNoStatus
-		for _, resource := range resources {
-			if st < statuses[resource] {
-				st = statuses[resource]
-			}
-		}
-		idStatus[id] = st
-	}
-	s.repo.Pull()
-	rendered, err := s.m.AppRendered(instanceId)
-	if err != nil {
-		http.Error(w, err.Error(), http.StatusInternalServerError)
-		return
-	}
-	idToResource, err := orginize(rendered)
-	if err != nil {
-		http.Error(w, err.Error(), http.StatusInternalServerError)
-		return
-	}
-	resources := []DodoResourceStatus{}
-	for id, st := range idStatus {
-		resources = append(resources, DodoResourceStatus{
-			Type:   idToResource[id].Type,
-			Name:   idToResource[id].Name,
-			Status: status.StatusString(st),
+	ret := []resourceStatus{}
+	for r, s := range statuses {
+		ret = append(ret, resourceStatus{
+			Type:   r.Type,
+			Name:   r.Name,
+			Status: status.StatusString(s),
 		})
 	}
-	json.NewEncoder(w).Encode(resources)
+	json.NewEncoder(w).Encode(ret)
 }
 
 type clustersData struct {
diff --git a/core/installer/status/helm.go b/core/installer/status/helm.go
index b2db1d4..8b4162c 100644
--- a/core/installer/status/helm.go
+++ b/core/installer/status/helm.go
@@ -3,6 +3,7 @@
 import (
 	"context"
 	"encoding/json"
+	"fmt"
 
 	"github.com/giolekva/pcloud/core/installer/kube"
 
@@ -12,7 +13,12 @@
 	"k8s.io/client-go/dynamic"
 )
 
-type helmRelease struct {
+type HelmRelease struct {
+	Metadata struct {
+		Name        string            `json:"name"`
+		Namespace   string            `json:"namespace"`
+		Annotations map[string]string `json:"annotations"`
+	} `json:"metadata"`
 	Status struct {
 		Conditions []struct {
 			Type   string `json:"type"`
@@ -26,6 +32,7 @@
 }
 
 func (m *helmReleaseMonitor) Get(ref ResourceRef) (Status, error) {
+	fmt.Printf("--- %+v\n", ref)
 	ctx := context.Background()
 	res, err := m.d.Resource(
 		schema.GroupVersionResource{
@@ -44,10 +51,17 @@
 	if err != nil {
 		return StatusNoStatus, err
 	}
-	var hr helmRelease
+	var hr HelmRelease
 	if err := json.Unmarshal(b, &hr); err != nil {
 		return StatusNoStatus, err
 	}
+	id, ok := hr.Metadata.Annotations["dodo.cloud/id"]
+	if !ok {
+		return StatusNoStatus, fmt.Errorf("missing annotation: dodo.cloud/id")
+	}
+	if id != ref.Id {
+		return StatusNotFound, nil
+	}
 	// TODO(gio): check more thoroughly
 	for _, c := range hr.Status.Conditions {
 		if c.Type == "Ready" && c.Status == "True" {
diff --git a/core/installer/status/instance.go b/core/installer/status/instance.go
index 285f68c..ad05e5a 100644
--- a/core/installer/status/instance.go
+++ b/core/installer/status/instance.go
@@ -1,28 +1,138 @@
 package status
 
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+)
+
 type InstanceMonitor struct {
 	m         Monitor
-	instances map[string][]Resource
+	instances map[string]ResourceOuts
 }
 
 func NewInstanceMonitor(m Monitor) *InstanceMonitor {
 	return &InstanceMonitor{
 		m:         m,
-		instances: map[string][]Resource{},
+		instances: map[string]ResourceOuts{},
 	}
 }
 
-func (m *InstanceMonitor) Monitor(id string, resources []Resource) {
+func (m *InstanceMonitor) Monitor(id string, resources ResourceOuts) {
 	m.instances[id] = resources
 }
 
-func (m *InstanceMonitor) Get(id string) (ret map[Resource]Status, err error) {
-	ret = map[Resource]Status{}
-	for _, r := range m.instances[id] {
-		ret[r], err = m.m.Get(r)
-		if err != nil {
-			break
+func (m *InstanceMonitor) Get(id string) (map[DodoResource]Status, error) {
+	ret := map[DodoResource]Status{}
+	namespace := m.instances[id].Release.Namespace
+	for _, out := range m.instances[id].Outs {
+		if _, err := m.monitor(namespace, DodoResource{"internal", out.Name}, out, ret); err != nil {
+			return nil, err
 		}
 	}
-	return
+	for i := range ret {
+		if i.Type == "internal" {
+			delete(ret, i)
+		}
+	}
+	return ret, nil
+}
+
+func mergeStatus(a Status, b Status) Status {
+	if a > b {
+		return a
+	} else {
+		return b
+	}
+}
+
+// TODO(gio): handle volume
+func (m *InstanceMonitor) monitor(namespace string, resource DodoResource, out ResourceOut, ret map[DodoResource]Status) (Status, error) {
+	status := StatusNoStatus
+	for _, h := range out.Helm {
+		hs, err := m.m.Get(Resource{
+			Type: ResourceHelmRelease,
+			ResourceRef: ResourceRef{
+				Id:        h.Id,
+				Name:      h.Name,
+				Namespace: namespace,
+			},
+		})
+		fmt.Println(hs, err)
+		if err != nil {
+			return StatusNoStatus, err
+		}
+		status = mergeStatus(status, hs)
+	}
+	for _, i := range out.PostgreSQL {
+		if s, err := m.monitor(namespace, DodoResource{"postgresql", i.Name}, i, ret); err != nil {
+			return StatusNoStatus, err
+		} else {
+			s = mergeStatus(status, s)
+		}
+	}
+	for _, i := range out.MongoDB {
+		if s, err := m.monitor(namespace, DodoResource{"mongodb", i.Name}, i, ret); err != nil {
+			return StatusNoStatus, err
+		} else {
+			s = mergeStatus(status, s)
+		}
+	}
+	for _, i := range out.Ingress {
+		name := fmt.Sprintf("https://%s.%s", i.Subdomain, i.Network.Domain)
+		if s, err := m.monitor(namespace, DodoResource{"ingress", name}, i.ResourceOut, ret); err != nil {
+			return StatusNoStatus, err
+		} else {
+			s = mergeStatus(status, s)
+		}
+	}
+	ret[resource] = status
+	return status, nil
+}
+
+type resourceIngress struct {
+	ResourceOut
+	Network struct {
+		Domain string `json:"domain"`
+	} `json:"network"`
+	Subdomain string `json:"subdomain"`
+}
+
+type resourceHelm struct {
+	Id   string `json:"id"`
+	Name string `json:"name"`
+}
+
+type ResourceOut struct {
+	Name       string                     `json:"name"`
+	PostgreSQL map[string]ResourceOut     `json:"postgresql"`
+	MongoDB    map[string]ResourceOut     `json:"mongodb"`
+	Ingress    map[string]resourceIngress `json:"ingress"`
+	Helm       map[string]resourceHelm    `json:"helmR"`
+}
+
+type ResourceOuts struct {
+	Release struct {
+		Namespace string `json:"namespace"`
+	}
+	Outs map[string]ResourceOut `json:"outs"`
+}
+
+type DodoResource struct {
+	Type string
+	Name string
+}
+
+type DodoResourceStatus struct {
+	Type   string `json:"type"`
+	Name   string `json:"name"`
+	Status string `json:"status"`
+}
+
+func DecodeResourceOuts(raw []byte) (ResourceOuts, error) {
+	var outs ResourceOuts
+	if err := json.NewDecoder(bytes.NewReader(raw)).Decode(&outs); err != nil {
+		return ResourceOuts{}, err
+	}
+	return outs, nil
 }
diff --git a/core/installer/status/instance_test.go b/core/installer/status/instance_test.go
new file mode 100644
index 0000000..6ed2ed2
--- /dev/null
+++ b/core/installer/status/instance_test.go
@@ -0,0 +1,1771 @@
+package status
+
+import (
+	"testing"
+)
+
+const contents = `
+{
+	"appType": "env",
+	"clusters": [
+		{
+			"name": "default",
+			"kubeconfig": "",
+			"ingressClassName": ""
+		}
+	],
+	"clusterMap": {
+		"default": {
+			"name": "default",
+			"kubeconfig": "",
+			"ingressClassName": ""
+		}
+	},
+	"global": {
+		"id": "hgrz",
+		"pcloudEnvName": "dodo",
+		"domain": "v1.dodo.cloud",
+		"privateDomain": "p.v1.dodo.cloud",
+		"contactEmail": "giolekva@gmail.com",
+		"adminPublicKey": "",
+		"publicIP": [
+			"135.181.48.180",
+			"65.108.39.172"
+		],
+		"nameserverIP": [
+			"135.181.48.180",
+			"65.108.39.172"
+		],
+		"namespacePrefix": "hgrz-",
+		"network": {
+			"dns": "10.0.1.2",
+			"dnsInClusterIP": "10.44.1.0",
+			"ingress": "10.0.1.0",
+			"headscale": "10.0.1.1",
+			"servicesFrom": "10.0.1.10",
+			"servicesTo": "10.0.1.254"
+		}
+	},
+	"input": {
+		"managerAddr": "",
+		"appId": "",
+		"key": {
+			"private": "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNl\nY2RzYS1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQQ7EA3CMrQs232Q7aTp\nLijbV/wKmF57z/YIY8nwyBU/23XwaW4eyKlJ6KHGmrlvON58gX79TUW2vi8drvnK\nFiWksuut5pJ98PTFoUkd7F2RVf/E5J7pumxbURiAZs7jdxoAAADQ3ikITt4pCE4A\nAAATZWNkc2Etc2hhMi1uaXN0cDM4NAAAAAhuaXN0cDM4NAAAAGEEOxANwjK0LNt9\nkO2k6S4o21f8Cphee8/2CGPJ8MgVP9t18GluHsipSeihxpq5bzjefIF+/U1Ftr4v\nHa75yhYlpLLrreaSffD0xaFJHexdkVX/xOSe6bpsW1EYgGbO43caAAAAMQDSIepG\ns5i9qNKLos7Hyg2Za/YHEC0OSOOVB55O4WY+Rf4sx7Fplnr79lgBY1DtkJ8AAAAA\nAQIDBAUGBw==\n-----END OPENSSH PRIVATE KEY-----\n",
+			"public": "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBDsQDcIytCzbfZDtpOkuKNtX/AqYXnvP9ghjyfDIFT/bdfBpbh7IqUnoocaauW843nyBfv1NRba+Lx2u+coWJaSy663mkn3w9MWhSR3sXZFV/8Tknum6bFtRGIBmzuN3Gg=="
+		}
+	},
+	"name": "dodo-app",
+	"description": "",
+	"readme": "",
+	"icon": "",
+	"namespace": "dodo-app",
+	"help": [],
+	"url": "",
+	"namespaces": [],
+	"release": {
+		"appInstanceId": "dodo-app-oem",
+		"namespace": "hgrz-dodo-app-oem",
+		"repoAddr": "ssh://soft-serve.hgrz.svc.cluster.local:22/config",
+		"appDir": "/apps/dodo-app-oem",
+		"imageRegistry": "docker.io"
+	},
+	"localCharts": {
+		"access-secrets": {
+			"chart": "helm-charts/access-secrets",
+			"sourceRef": {
+				"kind": "GitRepository",
+				"name": "hgrz",
+				"namespace": "hgrz"
+			}
+		},
+		"app": {
+			"chart": "helm-charts/app",
+			"sourceRef": {
+				"kind": "GitRepository",
+				"name": "hgrz",
+				"namespace": "hgrz"
+			}
+		},
+		"authProxy": {
+			"chart": "helm-charts/authProxy",
+			"sourceRef": {
+				"kind": "GitRepository",
+				"name": "hgrz",
+				"namespace": "hgrz"
+			}
+		},
+		"ingress": {
+			"chart": "helm-charts/ingress",
+			"sourceRef": {
+				"kind": "GitRepository",
+				"name": "hgrz",
+				"namespace": "hgrz"
+			}
+		},
+		"postgres": {
+			"chart": "helm-charts/postgres",
+			"sourceRef": {
+				"kind": "GitRepository",
+				"name": "hgrz",
+				"namespace": "hgrz"
+			}
+		},
+		"secret": {
+			"chart": "helm-charts/secret",
+			"sourceRef": {
+				"kind": "GitRepository",
+				"name": "hgrz",
+				"namespace": "hgrz"
+			}
+		},
+		"volume": {
+			"chart": "helm-charts/volume",
+			"sourceRef": {
+				"kind": "GitRepository",
+				"name": "hgrz",
+				"namespace": "hgrz"
+			}
+		}
+	},
+	"networks": {
+		"private": {
+			"name": "Private",
+			"ingressClass": "hgrz-ingress-private",
+			"domain": "p.v1.dodo.cloud",
+			"allocatePortAddr": "http://port-allocator.hgrz-ingress-private.svc.cluster.local/api/allocate",
+			"reservePortAddr": "http://port-allocator.hgrz-ingress-private.svc.cluster.local/api/reserve",
+			"deallocatePortAddr": "http://port-allocator.hgrz-ingress-private.svc.cluster.local/api/remove"
+		},
+		"public": {
+			"name": "Public",
+			"ingressClass": "dodo-ingress-public",
+			"certificateIssuer": "hgrz-public",
+			"domain": "v1.dodo.cloud",
+			"allocatePortAddr": "http://port-allocator.dodo-ingress-public.svc.cluster.local/api/allocate",
+			"reservePortAddr": "http://port-allocator.dodo-ingress-public.svc.cluster.local/api/reserve",
+			"deallocatePortAddr": "http://port-allocator.dodo-ingress-public.svc.cluster.local/api/remove"
+		}
+	},
+	"mongodb": [],
+	"envVars": [
+		"DODO_POSTGRESQL_DB_URL=postgresql://postgres:postgres@postgres-db.hgrz-dodo-app-oem.svc.cluster.local/postgres"
+	],
+	"service": [
+		{
+			"apiPort": 2000,
+			"ports": [
+				{
+					"name": "web",
+					"protocol": "TCP",
+					"value": 8080
+				}
+			],
+			"env": [
+				{
+					"name": "DODO_PORT_WEB"
+				},
+				{
+					"name": "DODO_POSTGRESQL_DB_URL"
+				}
+			],
+			"expose": [],
+			"ingress": [
+				{
+					"auth": {
+						"enabled": false
+					},
+					"network": "Private",
+					"port": {
+						"name": "web"
+					},
+					"subdomain": "t2",
+					"baseURL": "https://t2.p.v1.dodo.cloud"
+				}
+			],
+			"name": "blog",
+			"type": "nextjs:deno-2.0.0",
+			"rootDir": "/dodo-app",
+			"preBuildCommands": [],
+			"source": {
+				"branch": "master",
+				"repository": "git@github.com:giolekva/dodo-blog.git",
+				"rootDir": "/"
+			},
+			"lastCmdEnv": [
+				"DODO_POSTGRESQL_DB_URL=postgresql://postgres:postgres@postgres-db.hgrz-dodo-app-oem.svc.cluster.local/postgres",
+				"DODO_PORT_WEB=8080"
+			],
+			"runConfiguration": [
+				{
+					"bin": "/usr/bin/npm",
+					"args": [
+						"install"
+					],
+					"env": []
+				},
+				{
+					"bin": "/usr/bin/deno",
+					"args": [
+						"task",
+						"build"
+					],
+					"env": [
+						"DODO_POSTGRESQL_DB_URL=postgresql://postgres:postgres@postgres-db.hgrz-dodo-app-oem.svc.cluster.local/postgres",
+						"DODO_PORT_WEB=8080"
+					]
+				},
+				{
+					"bin": "/usr/bin/deno",
+					"args": [
+						"task",
+						"start"
+					],
+					"env": [
+						"DODO_POSTGRESQL_DB_URL=postgresql://postgres:postgres@postgres-db.hgrz-dodo-app-oem.svc.cluster.local/postgres",
+						"DODO_PORT_WEB=8080"
+					]
+				}
+			],
+			"volume": [],
+			"dev": {
+				"enabled": false
+			},
+			"vm": {
+				"cloudInit": {
+					"runCmd": [
+						[
+							"sh",
+							"-c",
+							"apt-get install -y npm unzip"
+						],
+						[
+							"sh",
+							"-c",
+							"curl -fsSL https://deno.land/install.sh | DENO_INSTALL=/usr/local/deno sh -s -- -y --no-modify-path"
+						]
+					],
+					"writeFiles": []
+				},
+				"env": [
+					"export PATH=$PATH:/usr/local/deno/bin"
+				]
+			}
+		}
+	],
+	"volume": [],
+	"postgresql": [
+		{
+			"name": "db",
+			"version": "15.3",
+			"initSQL": "",
+			"size": "1Gi",
+			"expose": []
+		}
+	],
+	"output": {
+		"openPort": [],
+		"images": {
+			"app": {
+				"registry": "docker.io",
+				"repository": "giolekva",
+				"name": "app-runner",
+				"tag": "nextjs-deno-2.0.0",
+				"pullPolicy": "Always",
+				"imageName": "giolekva/app-runner",
+				"fullName": "docker.io/giolekva/app-runner",
+				"fullNameWithTag": "docker.io/giolekva/app-runner:nextjs-deno-2.0.0"
+			},
+			"postgres": {
+				"registry": "docker.io",
+				"repository": "library",
+				"name": "postgres",
+				"tag": "15.3",
+				"pullPolicy": "IfNotPresent",
+				"imageName": "library/postgres",
+				"fullName": "docker.io/library/postgres",
+				"fullNameWithTag": "docker.io/library/postgres:15.3"
+			},
+			"authProxy": {
+				"registry": "docker.io",
+				"repository": "giolekva",
+				"name": "auth-proxy",
+				"tag": "latest",
+				"pullPolicy": "Always",
+				"imageName": "giolekva/auth-proxy",
+				"fullName": "docker.io/giolekva/auth-proxy",
+				"fullNameWithTag": "docker.io/giolekva/auth-proxy:latest"
+			},
+			"tailscale-proxy": {
+				"registry": "docker.io",
+				"repository": "tailscale",
+				"name": "tailscale",
+				"tag": "v1.82.0",
+				"pullPolicy": "IfNotPresent",
+				"imageName": "tailscale/tailscale",
+				"fullName": "docker.io/tailscale/tailscale",
+				"fullNameWithTag": "docker.io/tailscale/tailscale:v1.82.0"
+			}
+		},
+		"charts": {
+			"access-secrets": {
+				"name": "access-secrets",
+				"kind": "GitRepository",
+				"address": "https://code.v1.dodo.cloud/helm-charts",
+				"branch": "main",
+				"path": "charts/access-secrets"
+			},
+			"app": {
+				"name": "app",
+				"kind": "GitRepository",
+				"address": "https://code.v1.dodo.cloud/helm-charts",
+				"branch": "main",
+				"path": "charts/app-runner"
+			},
+			"postgres": {
+				"name": "postgres",
+				"kind": "GitRepository",
+				"address": "https://code.v1.dodo.cloud/helm-charts",
+				"branch": "main",
+				"path": "charts/postgresql"
+			},
+			"ingress": {
+				"name": "ingress",
+				"kind": "GitRepository",
+				"address": "https://code.v1.dodo.cloud/helm-charts",
+				"branch": "main",
+				"path": "charts/ingress"
+			},
+			"authProxy": {
+				"name": "authProxy",
+				"kind": "GitRepository",
+				"address": "https://code.v1.dodo.cloud/helm-charts",
+				"branch": "main",
+				"path": "charts/auth-proxy"
+			},
+			"volume": {
+				"name": "volume",
+				"kind": "GitRepository",
+				"address": "https://code.v1.dodo.cloud/helm-charts",
+				"branch": "main",
+				"path": "charts/volumes"
+			},
+			"secret": {
+				"name": "secret",
+				"kind": "GitRepository",
+				"address": "https://code.v1.dodo.cloud/helm-charts",
+				"branch": "main",
+				"path": "charts/secret"
+			}
+		},
+		"clusterProxy": {},
+		"helm": {
+			"postgres-db": {
+				"apiVersion": "helm.toolkit.fluxcd.io/v2beta1",
+				"kind": "HelmRelease",
+				"metadata": {
+					"name": "postgres-db",
+					"namespace": "hgrz-dodo-app-oem",
+					"annotations": {
+						"dodo.cloud/id": "t9W0Ivmbsoezk8/UrKXD6yLW7TLOCJLonJBUF0kFKiA=",
+						"dodo.cloud/installer-info": "",
+						"dodo.cloud/resource-type": "postgresql",
+						"dodo.cloud/resource.postgresql.name": "db",
+						"dodo.cloud/resource.postgresql.version": "15.3",
+						"dodo.cloud/resource.postgresql.volume": "db-postgresql"
+					}
+				},
+				"spec": {
+					"interval": "1m0s",
+					"dependsOn": [],
+					"chart": {
+						"spec": {
+							"chart": "helm-charts/postgres",
+							"sourceRef": {
+								"kind": "GitRepository",
+								"name": "hgrz",
+								"namespace": "hgrz"
+							}
+						}
+					},
+					"install": {
+						"remediation": {
+							"retries": -1
+						}
+					},
+					"upgrade": {
+						"remediation": {
+							"retries": -1
+						}
+					},
+					"values": {
+						"fullnameOverride": "postgres-db",
+						"image": {
+							"registry": "docker.io",
+							"repository": "library/postgres",
+							"tag": "15.3",
+							"pullPolicy": "IfNotPresent"
+						},
+						"auth": {
+							"postgresPassword": "postgres",
+							"username": "postgres",
+							"password": "postgres",
+							"database": "postgres"
+						},
+						"service": {
+							"type": "ClusterIP",
+							"port": 5432
+						},
+						"global": {
+							"postgresql": {
+								"auth": {
+									"postgresPassword": "postgres",
+									"username": "postgres",
+									"password": "postgres",
+									"database": "postgres"
+								}
+							}
+						},
+						"primary": {
+							"persistence": {
+								"existingClaim": "db-postgresql"
+							},
+							"securityContext": {
+								"enabled": true,
+								"fsGroup": 0
+							},
+							"containerSecurityContext": {
+								"enabled": true,
+								"runAsUser": 0
+							}
+						},
+						"volumePermissions": {
+							"securityContext": {
+								"runAsUser": 0
+							}
+						}
+					}
+				}
+			},
+			"db-postgresql-volume": {
+				"apiVersion": "helm.toolkit.fluxcd.io/v2beta1",
+				"kind": "HelmRelease",
+				"metadata": {
+					"name": "db-postgresql-volume",
+					"namespace": "hgrz-dodo-app-oem",
+					"annotations": {
+						"dodo.cloud/id": "t9W0Ivmbsoezk8/UrKXD6yLW7TLOCJLonJBUF0kFKiA=",
+						"dodo.cloud/installer-info": "Creating disk for db-postgresql",
+						"dodo.cloud/resource-type": "volume",
+						"dodo.cloud/resource.volume.name": "db-postgresql",
+						"dodo.cloud/resource.volume.size": "1Gi"
+					}
+				},
+				"spec": {
+					"interval": "1m0s",
+					"dependsOn": [],
+					"chart": {
+						"spec": {
+							"chart": "helm-charts/volume",
+							"sourceRef": {
+								"kind": "GitRepository",
+								"name": "hgrz",
+								"namespace": "hgrz"
+							}
+						}
+					},
+					"install": {
+						"remediation": {
+							"retries": -1
+						}
+					},
+					"upgrade": {
+						"remediation": {
+							"retries": -1
+						}
+					},
+					"values": {
+						"name": "db-postgresql",
+						"size": "1Gi",
+						"accessMode": "ReadWriteOnce"
+					}
+				}
+			},
+			"blog": {
+				"apiVersion": "helm.toolkit.fluxcd.io/v2beta1",
+				"kind": "HelmRelease",
+				"metadata": {
+					"name": "blog",
+					"namespace": "hgrz-dodo-app-oem",
+					"annotations": {
+						"dodo.cloud/id": "JSYKTmXOHvKo4Ecj+RThTMIE8uS7BcWA0vm2ydknN5s=",
+						"dodo.cloud/installer-info": "",
+						"dodo.cloud/resource-type": "service",
+						"dodo.cloud/resource.service.name": "blog"
+					}
+				},
+				"spec": {
+					"interval": "1m0s",
+					"dependsOn": [],
+					"chart": {
+						"spec": {
+							"chart": "helm-charts/app",
+							"sourceRef": {
+								"kind": "GitRepository",
+								"name": "hgrz",
+								"namespace": "hgrz"
+							}
+						}
+					},
+					"install": {
+						"remediation": {
+							"retries": -1
+						}
+					},
+					"upgrade": {
+						"remediation": {
+							"retries": -1
+						}
+					},
+					"values": {
+						"image": {
+							"repository": "docker.io/giolekva/app-runner",
+							"tag": "nextjs-deno-2.0.0",
+							"pullPolicy": "Always"
+						},
+						"runtimeClassName": "untrusted-external",
+						"name": "blog",
+						"apiPort": 2000,
+						"appPorts": [
+							{
+								"name": "web",
+								"containerPort": 8080,
+								"protocol": "TCP"
+							}
+						],
+						"appDir": "/dodo-app",
+						"appId": "",
+						"repoAddr": "git@github.com:giolekva/dodo-blog.git",
+						"branch": "master",
+						"rootDir": "/",
+						"sshPrivateKey": "LS0tLS1CRUdJTiBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0KYjNCbGJuTnphQzFyWlhrdGRqRUFBQUFBQkc1dmJtVUFBQUFFYm05dVpRQUFBQUFBQUFBQkFBQUFpQUFBQUJObApZMlJ6WVMxemFHRXlMVzVwYzNSd016ZzBBQUFBQ0c1cGMzUndNemcwQUFBQVlRUTdFQTNDTXJRczIzMlE3YVRwCkxpamJWL3dLbUY1N3ovWUlZOG53eUJVLzIzWHdhVzRleUtsSjZLSEdtcmx2T041OGdYNzlUVVcydmk4ZHJ2bksKRmlXa3N1dXQ1cEo5OFBURm9Va2Q3RjJSVmYvRTVKN3B1bXhiVVJpQVpzN2pkeG9BQUFEUTNpa0lUdDRwQ0U0QQpBQUFUWldOa2MyRXRjMmhoTWkxdWFYTjBjRE00TkFBQUFBaHVhWE4wY0RNNE5BQUFBR0VFT3hBTndqSzBMTnQ5CmtPMms2UzRvMjFmOENwaGVlOC8yQ0dQSjhNZ1ZQOXQxOEdsdUhzaXBTZWloeHBxNWJ6amVmSUYrL1UxRnRyNHYKSGE3NXloWWxwTExycmVhU2ZmRDB4YUZKSGV4ZGtWWC94T1NlNmJwc1cxRVlnR2JPNDNjYUFBQUFNUURTSWVwRwpzNWk5cU5LTG9zN0h5ZzJaYS9ZSEVDME9TT09WQjU1TzRXWStSZjRzeDdGcGxucjc5bGdCWTFEdGtKOEFBQUFBCkFRSURCQVVHQnc9PQotLS0tLUVORCBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0K",
+						"runCfg": "W3siYmluIjoiL3Vzci9iaW4vbnBtIiwiYXJncyI6WyJpbnN0YWxsIl0sImVudiI6W119LHsiYmluIjoiL3Vzci9iaW4vZGVubyIsImFyZ3MiOlsidGFzayIsImJ1aWxkIl0sImVudiI6WyJET0RPX1BPU1RHUkVTUUxfREJfVVJMPXBvc3RncmVzcWw6Ly9wb3N0Z3Jlczpwb3N0Z3Jlc0Bwb3N0Z3Jlcy1kYi5oZ3J6LWRvZG8tYXBwLW9lbS5zdmMuY2x1c3Rlci5sb2NhbC9wb3N0Z3JlcyIsIkRPRE9fUE9SVF9XRUI9ODA4MCJdfSx7ImJpbiI6Ii91c3IvYmluL2Rlbm8iLCJhcmdzIjpbInRhc2siLCJzdGFydCJdLCJlbnYiOlsiRE9ET19QT1NUR1JFU1FMX0RCX1VSTD1wb3N0Z3Jlc3FsOi8vcG9zdGdyZXM6cG9zdGdyZXNAcG9zdGdyZXMtZGIuaGdyei1kb2RvLWFwcC1vZW0uc3ZjLmNsdXN0ZXIubG9jYWwvcG9zdGdyZXMiLCJET0RPX1BPUlRfV0VCPTgwODAiXX1d",
+						"managerAddr": "",
+						"volumes": []
+					}
+				}
+			},
+			"blog-0-ingress": {
+				"apiVersion": "helm.toolkit.fluxcd.io/v2beta1",
+				"kind": "HelmRelease",
+				"metadata": {
+					"name": "blog-0-ingress",
+					"namespace": "hgrz-dodo-app-oem",
+					"annotations": {
+						"dodo.cloud/id": "XYqWfRvGkulShM6YqrnWRWqqhQ3dBmjlUIDY2LMW9wo=",
+						"dodo.cloud/installer-info": "Generating TLS certificate for https://t2.p.v1.dodo.cloud",
+						"dodo.cloud/resource-type": "ingress",
+						"dodo.cloud/resource.ingress.host": "https://t2.p.v1.dodo.cloud",
+						"dodo.cloud/resource.ingress.home": "",
+						"dodo.cloud/resource.ingress.name": "blog"
+					}
+				},
+				"spec": {
+					"interval": "1m0s",
+					"dependsOn": [],
+					"chart": {
+						"spec": {
+							"chart": "helm-charts/ingress",
+							"sourceRef": {
+								"kind": "GitRepository",
+								"name": "hgrz",
+								"namespace": "hgrz"
+							}
+						}
+					},
+					"install": {
+						"remediation": {
+							"retries": -1
+						}
+					},
+					"upgrade": {
+						"remediation": {
+							"retries": -1
+						}
+					},
+					"values": {
+						"domain": "t2.p.v1.dodo.cloud",
+						"appRoot": "",
+						"ingressClassName": "hgrz-ingress-private",
+						"certificateIssuer": "",
+						"service": {
+							"name": "blog-app",
+							"port": {
+								"number": 8080
+							}
+						}
+					}
+				}
+			}
+		}
+	},
+	"outs": {
+		"out": {
+			"id": "gP/o6bUhK8yahxnHVq/ARX8rZ06UYvXozT4T5zJFrR4=",
+			"ingress": {},
+			"vm": {},
+			"charts": {
+				"postgres": {
+					"name": "postgres",
+					"kind": "GitRepository",
+					"address": "https://code.v1.dodo.cloud/helm-charts",
+					"branch": "main",
+					"path": "charts/postgresql"
+				},
+				"volume": {
+					"name": "volume",
+					"kind": "GitRepository",
+					"address": "https://code.v1.dodo.cloud/helm-charts",
+					"branch": "main",
+					"path": "charts/volumes"
+				},
+				"secret": {
+					"name": "secret",
+					"kind": "GitRepository",
+					"address": "https://code.v1.dodo.cloud/helm-charts",
+					"branch": "main",
+					"path": "charts/secret"
+				}
+			},
+			"volume": {},
+			"postgresql": {
+				"db": {
+					"id": "t9W0Ivmbsoezk8/UrKXD6yLW7TLOCJLonJBUF0kFKiA=",
+					"name": "db",
+					"version": "15.3",
+					"initSQL": "",
+					"size": "1Gi",
+					"expose": [],
+					"ingress": {},
+					"mongodb": {},
+					"postgresql": {},
+					"vm": {},
+					"charts": {
+						"postgres": {
+							"name": "postgres",
+							"kind": "GitRepository",
+							"address": "https://code.v1.dodo.cloud/helm-charts",
+							"branch": "main",
+							"path": "charts/postgresql"
+						},
+						"volume": {
+							"name": "volume",
+							"kind": "GitRepository",
+							"address": "https://code.v1.dodo.cloud/helm-charts",
+							"branch": "main",
+							"path": "charts/volumes"
+						},
+						"secret": {
+							"name": "secret",
+							"kind": "GitRepository",
+							"address": "https://code.v1.dodo.cloud/helm-charts",
+							"branch": "main",
+							"path": "charts/secret"
+						}
+					},
+					"volume": {
+						"db-postgresql": {
+							"name": "db-postgresql",
+							"size": "1Gi",
+							"accessMode": "ReadWriteOnce"
+						}
+					},
+					"helm": {
+						"postgres-db": {
+							"chart": {
+								"name": "postgres",
+								"kind": "GitRepository",
+								"address": "https://code.v1.dodo.cloud/helm-charts",
+								"branch": "main",
+								"path": "charts/postgresql"
+							},
+							"name": "postgres-db",
+							"dependsOn": [],
+							"info": "",
+							"annotations": {
+								"dodo.cloud/resource-type": "postgresql",
+								"dodo.cloud/resource.postgresql.name": "db",
+								"dodo.cloud/resource.postgresql.version": "15.3",
+								"dodo.cloud/resource.postgresql.volume": "db-postgresql"
+							},
+							"values": {
+								"fullnameOverride": "postgres-db",
+								"image": {
+									"registry": "docker.io",
+									"repository": "library/postgres",
+									"tag": "15.3",
+									"pullPolicy": "IfNotPresent"
+								},
+								"auth": {
+									"postgresPassword": "postgres",
+									"username": "postgres",
+									"password": "postgres",
+									"database": "postgres"
+								},
+								"service": {
+									"type": "ClusterIP",
+									"port": 5432
+								},
+								"global": {
+									"postgresql": {
+										"auth": {
+											"postgresPassword": "postgres",
+											"username": "postgres",
+											"password": "postgres",
+											"database": "postgres"
+										}
+									}
+								},
+								"primary": {
+									"persistence": {
+										"existingClaim": "db-postgresql"
+									},
+									"securityContext": {
+										"enabled": true,
+										"fsGroup": 0
+									},
+									"containerSecurityContext": {
+										"enabled": true,
+										"runAsUser": 0
+									}
+								},
+								"volumePermissions": {
+									"securityContext": {
+										"runAsUser": 0
+									}
+								}
+							}
+						},
+						"db-postgresql-volume": {
+							"chart": {
+								"name": "volume",
+								"kind": "GitRepository",
+								"address": "https://code.v1.dodo.cloud/helm-charts",
+								"branch": "main",
+								"path": "charts/volumes"
+							},
+							"name": "db-postgresql-volume",
+							"dependsOn": [],
+							"info": "Creating disk for db-postgresql",
+							"annotations": {
+								"dodo.cloud/resource-type": "volume",
+								"dodo.cloud/resource.volume.name": "db-postgresql",
+								"dodo.cloud/resource.volume.size": "1Gi"
+							},
+							"values": {
+								"name": "db-postgresql",
+								"size": "1Gi",
+								"accessMode": "ReadWriteOnce"
+							}
+						}
+					},
+					"openPort": [],
+					"openPortMap": {
+						"_self": []
+					},
+					"clusterProxy": {},
+					"images": {
+						"postgres": {
+							"registry": "docker.io",
+							"repository": "library",
+							"name": "postgres",
+							"tag": "15.3",
+							"pullPolicy": "IfNotPresent",
+							"imageName": "library/postgres",
+							"fullName": "docker.io/library/postgres",
+							"fullNameWithTag": "docker.io/library/postgres:15.3"
+						}
+					},
+					"helmR": {
+						"postgres-db": {
+							"chart": {
+								"name": "postgres",
+								"kind": "GitRepository",
+								"address": "https://code.v1.dodo.cloud/helm-charts",
+								"branch": "main",
+								"path": "charts/postgresql"
+							},
+							"id": "t9W0Ivmbsoezk8/UrKXD6yLW7TLOCJLonJBUF0kFKiA=",
+							"name": "postgres-db",
+							"dependsOn": [],
+							"info": "",
+							"annotations": {
+								"dodo.cloud/resource-type": "postgresql",
+								"dodo.cloud/resource.postgresql.name": "db",
+								"dodo.cloud/resource.postgresql.version": "15.3",
+								"dodo.cloud/resource.postgresql.volume": "db-postgresql"
+							},
+							"values": {
+								"fullnameOverride": "postgres-db",
+								"image": {
+									"registry": "docker.io",
+									"repository": "library/postgres",
+									"tag": "15.3",
+									"pullPolicy": "IfNotPresent"
+								},
+								"auth": {
+									"postgresPassword": "postgres",
+									"username": "postgres",
+									"password": "postgres",
+									"database": "postgres"
+								},
+								"service": {
+									"type": "ClusterIP",
+									"port": 5432
+								},
+								"global": {
+									"postgresql": {
+										"auth": {
+											"postgresPassword": "postgres",
+											"username": "postgres",
+											"password": "postgres",
+											"database": "postgres"
+										}
+									}
+								},
+								"primary": {
+									"persistence": {
+										"existingClaim": "db-postgresql"
+									},
+									"securityContext": {
+										"enabled": true,
+										"fsGroup": 0
+									},
+									"containerSecurityContext": {
+										"enabled": true,
+										"runAsUser": 0
+									}
+								},
+								"volumePermissions": {
+									"securityContext": {
+										"runAsUser": 0
+									}
+								}
+							}
+						},
+						"db-postgresql-volume": {
+							"chart": {
+								"name": "volume",
+								"kind": "GitRepository",
+								"address": "https://code.v1.dodo.cloud/helm-charts",
+								"branch": "main",
+								"path": "charts/volumes"
+							},
+							"id": "t9W0Ivmbsoezk8/UrKXD6yLW7TLOCJLonJBUF0kFKiA=",
+							"name": "db-postgresql-volume",
+							"dependsOn": [],
+							"info": "Creating disk for db-postgresql",
+							"annotations": {
+								"dodo.cloud/resource-type": "volume",
+								"dodo.cloud/resource.volume.name": "db-postgresql",
+								"dodo.cloud/resource.volume.size": "1Gi"
+							},
+							"values": {
+								"name": "db-postgresql",
+								"size": "1Gi",
+								"accessMode": "ReadWriteOnce"
+							}
+						}
+					}
+				}
+			},
+			"mongodb": {},
+			"openPortMap": {
+				"_self": [],
+				"postgresql-db": {
+					"_self": []
+				}
+			},
+			"clusterProxy": {},
+			"images": {
+				"postgres": {
+					"registry": "docker.io",
+					"repository": "library",
+					"name": "postgres",
+					"tag": "15.3",
+					"pullPolicy": "IfNotPresent",
+					"imageName": "library/postgres",
+					"fullName": "docker.io/library/postgres",
+					"fullNameWithTag": "docker.io/library/postgres:15.3"
+				}
+			},
+			"helm": {},
+			"openPort": [],
+			"helmR": {
+				"postgres-db": {
+					"chart": {
+						"name": "postgres",
+						"kind": "GitRepository",
+						"address": "https://code.v1.dodo.cloud/helm-charts",
+						"branch": "main",
+						"path": "charts/postgresql"
+					},
+					"id": "t9W0Ivmbsoezk8/UrKXD6yLW7TLOCJLonJBUF0kFKiA=",
+					"name": "postgres-db",
+					"dependsOn": [],
+					"info": "",
+					"annotations": {
+						"dodo.cloud/resource-type": "postgresql",
+						"dodo.cloud/resource.postgresql.name": "db",
+						"dodo.cloud/resource.postgresql.version": "15.3",
+						"dodo.cloud/resource.postgresql.volume": "db-postgresql"
+					},
+					"values": {
+						"fullnameOverride": "postgres-db",
+						"image": {
+							"registry": "docker.io",
+							"repository": "library/postgres",
+							"tag": "15.3",
+							"pullPolicy": "IfNotPresent"
+						},
+						"auth": {
+							"postgresPassword": "postgres",
+							"username": "postgres",
+							"password": "postgres",
+							"database": "postgres"
+						},
+						"service": {
+							"type": "ClusterIP",
+							"port": 5432
+						},
+						"global": {
+							"postgresql": {
+								"auth": {
+									"postgresPassword": "postgres",
+									"username": "postgres",
+									"password": "postgres",
+									"database": "postgres"
+								}
+							}
+						},
+						"primary": {
+							"persistence": {
+								"existingClaim": "db-postgresql"
+							},
+							"securityContext": {
+								"enabled": true,
+								"fsGroup": 0
+							},
+							"containerSecurityContext": {
+								"enabled": true,
+								"runAsUser": 0
+							}
+						},
+						"volumePermissions": {
+							"securityContext": {
+								"runAsUser": 0
+							}
+						}
+					}
+				},
+				"db-postgresql-volume": {
+					"chart": {
+						"name": "volume",
+						"kind": "GitRepository",
+						"address": "https://code.v1.dodo.cloud/helm-charts",
+						"branch": "main",
+						"path": "charts/volumes"
+					},
+					"id": "t9W0Ivmbsoezk8/UrKXD6yLW7TLOCJLonJBUF0kFKiA=",
+					"name": "db-postgresql-volume",
+					"dependsOn": [],
+					"info": "Creating disk for db-postgresql",
+					"annotations": {
+						"dodo.cloud/resource-type": "volume",
+						"dodo.cloud/resource.volume.name": "db-postgresql",
+						"dodo.cloud/resource.volume.size": "1Gi"
+					},
+					"values": {
+						"name": "db-postgresql",
+						"size": "1Gi",
+						"accessMode": "ReadWriteOnce"
+					}
+				}
+			}
+		},
+		"service_blog": {
+			"envProfile": "export PATH=$PATH:/usr/local/deno/bin\nexport DODO_POSTGRESQL_DB_URL=postgresql://postgres:postgres@postgres-db.hgrz-dodo-app-oem.svc.cluster.local/postgres\nexport DODO_PORT_WEB=8080",
+			"id": "oE1Bj99zy2UGOVe7G1cXNCa45NJnE+KY7yopklepsKo=",
+			"ingress": {
+				"blog-0": {
+					"id": "s6SF2Y9VxueisZ4sGyEp4evD5mGcOTg3Aqwygl3+bVA=",
+					"name": "blog-0",
+					"auth": {
+						"enabled": false,
+						"groups": "",
+						"noAuthPathPatterns": []
+					},
+					"network": {
+						"name": "Private",
+						"ingressClass": "hgrz-ingress-private",
+						"certificateIssuer": "",
+						"domain": "p.v1.dodo.cloud",
+						"allocatePortAddr": "http://port-allocator.hgrz-ingress-private.svc.cluster.local/api/allocate",
+						"reservePortAddr": "http://port-allocator.hgrz-ingress-private.svc.cluster.local/api/reserve",
+						"deallocatePortAddr": "http://port-allocator.hgrz-ingress-private.svc.cluster.local/api/remove"
+					},
+					"subdomain": "t2",
+					"home": "",
+					"label": "blog",
+					"appRoot": "",
+					"service": {
+						"name": "blog-app",
+						"port": 8080
+					},
+					"g": {
+						"id": "hgrz",
+						"pcloudEnvName": "dodo",
+						"domain": "v1.dodo.cloud",
+						"privateDomain": "p.v1.dodo.cloud",
+						"contactEmail": "giolekva@gmail.com",
+						"adminPublicKey": "",
+						"publicIP": [
+							"135.181.48.180",
+							"65.108.39.172"
+						],
+						"nameserverIP": [
+							"135.181.48.180",
+							"65.108.39.172"
+						],
+						"namespacePrefix": "hgrz-",
+						"network": {
+							"dns": "10.0.1.2",
+							"dnsInClusterIP": "10.44.1.0",
+							"ingress": "10.0.1.0",
+							"headscale": "10.0.1.1",
+							"servicesFrom": "10.0.1.10",
+							"servicesTo": "10.0.1.254"
+						}
+					},
+					"ingress": {},
+					"mongodb": {},
+					"postgresql": {},
+					"vm": {},
+					"charts": {
+						"volume": {
+							"name": "volume",
+							"kind": "GitRepository",
+							"address": "https://code.v1.dodo.cloud/helm-charts",
+							"branch": "main",
+							"path": "charts/volumes"
+						},
+						"secret": {
+							"name": "secret",
+							"kind": "GitRepository",
+							"address": "https://code.v1.dodo.cloud/helm-charts",
+							"branch": "main",
+							"path": "charts/secret"
+						},
+						"ingress": {
+							"name": "ingress",
+							"kind": "GitRepository",
+							"address": "https://code.v1.dodo.cloud/helm-charts",
+							"branch": "main",
+							"path": "charts/ingress"
+						},
+						"authProxy": {
+							"name": "authProxy",
+							"kind": "GitRepository",
+							"address": "https://code.v1.dodo.cloud/helm-charts",
+							"branch": "main",
+							"path": "charts/auth-proxy"
+						}
+					},
+					"volume": {},
+					"helm": {
+						"blog-0-ingress": {
+							"chart": {
+								"name": "ingress",
+								"kind": "GitRepository",
+								"address": "https://code.v1.dodo.cloud/helm-charts",
+								"branch": "main",
+								"path": "charts/ingress"
+							},
+							"name": "blog-0-ingress",
+							"dependsOn": [],
+							"cluster": null,
+							"info": "Generating TLS certificate for https://t2.p.v1.dodo.cloud",
+							"annotations": {
+								"dodo.cloud/resource-type": "ingress",
+								"dodo.cloud/resource.ingress.host": "https://t2.p.v1.dodo.cloud",
+								"dodo.cloud/resource.ingress.home": "",
+								"dodo.cloud/resource.ingress.name": "blog"
+							},
+							"values": {
+								"domain": "t2.p.v1.dodo.cloud",
+								"appRoot": "",
+								"ingressClassName": "hgrz-ingress-private",
+								"certificateIssuer": "",
+								"service": {
+									"name": "blog-app",
+									"port": {
+										"number": 8080
+									}
+								}
+							}
+						}
+					},
+					"openPort": [],
+					"openPortMap": {
+						"_self": []
+					},
+					"clusterProxy": {},
+					"images": {
+						"authProxy": {
+							"registry": "docker.io",
+							"repository": "giolekva",
+							"name": "auth-proxy",
+							"tag": "latest",
+							"pullPolicy": "Always",
+							"imageName": "giolekva/auth-proxy",
+							"fullName": "docker.io/giolekva/auth-proxy",
+							"fullNameWithTag": "docker.io/giolekva/auth-proxy:latest"
+						}
+					},
+					"helmR": {
+						"blog-0-ingress": {
+							"chart": {
+								"name": "ingress",
+								"kind": "GitRepository",
+								"address": "https://code.v1.dodo.cloud/helm-charts",
+								"branch": "main",
+								"path": "charts/ingress"
+							},
+							"id": "s6SF2Y9VxueisZ4sGyEp4evD5mGcOTg3Aqwygl3+bVA=",
+							"name": "blog-0-ingress",
+							"dependsOn": [],
+							"cluster": null,
+							"info": "Generating TLS certificate for https://t2.p.v1.dodo.cloud",
+							"annotations": {
+								"dodo.cloud/resource-type": "ingress",
+								"dodo.cloud/resource.ingress.host": "https://t2.p.v1.dodo.cloud",
+								"dodo.cloud/resource.ingress.home": "",
+								"dodo.cloud/resource.ingress.name": "blog"
+							},
+							"values": {
+								"domain": "t2.p.v1.dodo.cloud",
+								"appRoot": "",
+								"ingressClassName": "hgrz-ingress-private",
+								"certificateIssuer": "",
+								"service": {
+									"name": "blog-app",
+									"port": {
+										"number": 8080
+									}
+								}
+							}
+						}
+					}
+				}
+			},
+			"mongodb": {},
+			"postgresql": {},
+			"vm": {},
+			"charts": {
+				"volume": {
+					"name": "volume",
+					"kind": "GitRepository",
+					"address": "https://code.v1.dodo.cloud/helm-charts",
+					"branch": "main",
+					"path": "charts/volumes"
+				},
+				"secret": {
+					"name": "secret",
+					"kind": "GitRepository",
+					"address": "https://code.v1.dodo.cloud/helm-charts",
+					"branch": "main",
+					"path": "charts/secret"
+				},
+				"access-secrets": {
+					"name": "access-secrets",
+					"kind": "GitRepository",
+					"address": "https://code.v1.dodo.cloud/helm-charts",
+					"branch": "main",
+					"path": "charts/access-secrets"
+				},
+				"app": {
+					"name": "app",
+					"kind": "GitRepository",
+					"address": "https://code.v1.dodo.cloud/helm-charts",
+					"branch": "main",
+					"path": "charts/app-runner"
+				},
+				"ingress": {
+					"name": "ingress",
+					"kind": "GitRepository",
+					"address": "https://code.v1.dodo.cloud/helm-charts",
+					"branch": "main",
+					"path": "charts/ingress"
+				},
+				"authProxy": {
+					"name": "authProxy",
+					"kind": "GitRepository",
+					"address": "https://code.v1.dodo.cloud/helm-charts",
+					"branch": "main",
+					"path": "charts/auth-proxy"
+				}
+			},
+			"volume": {},
+			"helm": {
+				"blog": {
+					"name": "blog",
+					"dependsOn": [],
+					"info": "",
+					"annotations": {
+						"dodo.cloud/resource-type": "service",
+						"dodo.cloud/resource.service.name": "blog"
+					},
+					"chart": {
+						"name": "app",
+						"kind": "GitRepository",
+						"address": "https://code.v1.dodo.cloud/helm-charts",
+						"branch": "main",
+						"path": "charts/app-runner"
+					},
+					"values": {
+						"image": {
+							"repository": "docker.io/giolekva/app-runner",
+							"tag": "nextjs-deno-2.0.0",
+							"pullPolicy": "Always"
+						},
+						"runtimeClassName": "untrusted-external",
+						"name": "blog",
+						"apiPort": 2000,
+						"appPorts": [
+							{
+								"name": "web",
+								"containerPort": 8080,
+								"protocol": "TCP"
+							}
+						],
+						"appDir": "/dodo-app",
+						"appId": "",
+						"repoAddr": "git@github.com:giolekva/dodo-blog.git",
+						"branch": "master",
+						"rootDir": "/",
+						"sshPrivateKey": "LS0tLS1CRUdJTiBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0KYjNCbGJuTnphQzFyWlhrdGRqRUFBQUFBQkc1dmJtVUFBQUFFYm05dVpRQUFBQUFBQUFBQkFBQUFpQUFBQUJObApZMlJ6WVMxemFHRXlMVzVwYzNSd016ZzBBQUFBQ0c1cGMzUndNemcwQUFBQVlRUTdFQTNDTXJRczIzMlE3YVRwCkxpamJWL3dLbUY1N3ovWUlZOG53eUJVLzIzWHdhVzRleUtsSjZLSEdtcmx2T041OGdYNzlUVVcydmk4ZHJ2bksKRmlXa3N1dXQ1cEo5OFBURm9Va2Q3RjJSVmYvRTVKN3B1bXhiVVJpQVpzN2pkeG9BQUFEUTNpa0lUdDRwQ0U0QQpBQUFUWldOa2MyRXRjMmhoTWkxdWFYTjBjRE00TkFBQUFBaHVhWE4wY0RNNE5BQUFBR0VFT3hBTndqSzBMTnQ5CmtPMms2UzRvMjFmOENwaGVlOC8yQ0dQSjhNZ1ZQOXQxOEdsdUhzaXBTZWloeHBxNWJ6amVmSUYrL1UxRnRyNHYKSGE3NXloWWxwTExycmVhU2ZmRDB4YUZKSGV4ZGtWWC94T1NlNmJwc1cxRVlnR2JPNDNjYUFBQUFNUURTSWVwRwpzNWk5cU5LTG9zN0h5ZzJaYS9ZSEVDME9TT09WQjU1TzRXWStSZjRzeDdGcGxucjc5bGdCWTFEdGtKOEFBQUFBCkFRSURCQVVHQnc9PQotLS0tLUVORCBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0K",
+						"runCfg": "W3siYmluIjoiL3Vzci9iaW4vbnBtIiwiYXJncyI6WyJpbnN0YWxsIl0sImVudiI6W119LHsiYmluIjoiL3Vzci9iaW4vZGVubyIsImFyZ3MiOlsidGFzayIsImJ1aWxkIl0sImVudiI6WyJET0RPX1BPU1RHUkVTUUxfREJfVVJMPXBvc3RncmVzcWw6Ly9wb3N0Z3Jlczpwb3N0Z3Jlc0Bwb3N0Z3Jlcy1kYi5oZ3J6LWRvZG8tYXBwLW9lbS5zdmMuY2x1c3Rlci5sb2NhbC9wb3N0Z3JlcyIsIkRPRE9fUE9SVF9XRUI9ODA4MCJdfSx7ImJpbiI6Ii91c3IvYmluL2Rlbm8iLCJhcmdzIjpbInRhc2siLCJzdGFydCJdLCJlbnYiOlsiRE9ET19QT1NUR1JFU1FMX0RCX1VSTD1wb3N0Z3Jlc3FsOi8vcG9zdGdyZXM6cG9zdGdyZXNAcG9zdGdyZXMtZGIuaGdyei1kb2RvLWFwcC1vZW0uc3ZjLmNsdXN0ZXIubG9jYWwvcG9zdGdyZXMiLCJET0RPX1BPUlRfV0VCPTgwODAiXX1d",
+						"managerAddr": "",
+						"volumes": []
+					}
+				}
+			},
+			"openPort": [],
+			"openPortMap": {
+				"_self": []
+			},
+			"clusterProxy": {},
+			"images": {
+				"app": {
+					"registry": "docker.io",
+					"repository": "giolekva",
+					"name": "app-runner",
+					"tag": "nextjs-deno-2.0.0",
+					"pullPolicy": "Always",
+					"imageName": "giolekva/app-runner",
+					"fullName": "docker.io/giolekva/app-runner",
+					"fullNameWithTag": "docker.io/giolekva/app-runner:nextjs-deno-2.0.0"
+				},
+				"tailscale-proxy": {
+					"registry": "docker.io",
+					"repository": "tailscale",
+					"name": "tailscale",
+					"tag": "v1.82.0",
+					"pullPolicy": "IfNotPresent",
+					"imageName": "tailscale/tailscale",
+					"fullName": "docker.io/tailscale/tailscale",
+					"fullNameWithTag": "docker.io/tailscale/tailscale:v1.82.0"
+				},
+				"authProxy": {
+					"registry": "docker.io",
+					"repository": "giolekva",
+					"name": "auth-proxy",
+					"tag": "latest",
+					"pullPolicy": "Always",
+					"imageName": "giolekva/auth-proxy",
+					"fullName": "docker.io/giolekva/auth-proxy",
+					"fullNameWithTag": "docker.io/giolekva/auth-proxy:latest"
+				}
+			},
+			"helmR": {
+				"blog": {
+					"chart": {
+						"name": "app",
+						"kind": "GitRepository",
+						"address": "https://code.v1.dodo.cloud/helm-charts",
+						"branch": "main",
+						"path": "charts/app-runner"
+					},
+					"id": "oE1Bj99zy2UGOVe7G1cXNCa45NJnE+KY7yopklepsKo=",
+					"name": "blog",
+					"dependsOn": [],
+					"info": "",
+					"annotations": {
+						"dodo.cloud/resource-type": "service",
+						"dodo.cloud/resource.service.name": "blog"
+					},
+					"values": {
+						"image": {
+							"repository": "docker.io/giolekva/app-runner",
+							"tag": "nextjs-deno-2.0.0",
+							"pullPolicy": "Always"
+						},
+						"runtimeClassName": "untrusted-external",
+						"name": "blog",
+						"apiPort": 2000,
+						"appPorts": [
+							{
+								"name": "web",
+								"containerPort": 8080,
+								"protocol": "TCP"
+							}
+						],
+						"appDir": "/dodo-app",
+						"appId": "",
+						"repoAddr": "git@github.com:giolekva/dodo-blog.git",
+						"branch": "master",
+						"rootDir": "/",
+						"sshPrivateKey": "LS0tLS1CRUdJTiBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0KYjNCbGJuTnphQzFyWlhrdGRqRUFBQUFBQkc1dmJtVUFBQUFFYm05dVpRQUFBQUFBQUFBQkFBQUFpQUFBQUJObApZMlJ6WVMxemFHRXlMVzVwYzNSd016ZzBBQUFBQ0c1cGMzUndNemcwQUFBQVlRUTdFQTNDTXJRczIzMlE3YVRwCkxpamJWL3dLbUY1N3ovWUlZOG53eUJVLzIzWHdhVzRleUtsSjZLSEdtcmx2T041OGdYNzlUVVcydmk4ZHJ2bksKRmlXa3N1dXQ1cEo5OFBURm9Va2Q3RjJSVmYvRTVKN3B1bXhiVVJpQVpzN2pkeG9BQUFEUTNpa0lUdDRwQ0U0QQpBQUFUWldOa2MyRXRjMmhoTWkxdWFYTjBjRE00TkFBQUFBaHVhWE4wY0RNNE5BQUFBR0VFT3hBTndqSzBMTnQ5CmtPMms2UzRvMjFmOENwaGVlOC8yQ0dQSjhNZ1ZQOXQxOEdsdUhzaXBTZWloeHBxNWJ6amVmSUYrL1UxRnRyNHYKSGE3NXloWWxwTExycmVhU2ZmRDB4YUZKSGV4ZGtWWC94T1NlNmJwc1cxRVlnR2JPNDNjYUFBQUFNUURTSWVwRwpzNWk5cU5LTG9zN0h5ZzJaYS9ZSEVDME9TT09WQjU1TzRXWStSZjRzeDdGcGxucjc5bGdCWTFEdGtKOEFBQUFBCkFRSURCQVVHQnc9PQotLS0tLUVORCBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0K",
+						"runCfg": "W3siYmluIjoiL3Vzci9iaW4vbnBtIiwiYXJncyI6WyJpbnN0YWxsIl0sImVudiI6W119LHsiYmluIjoiL3Vzci9iaW4vZGVubyIsImFyZ3MiOlsidGFzayIsImJ1aWxkIl0sImVudiI6WyJET0RPX1BPU1RHUkVTUUxfREJfVVJMPXBvc3RncmVzcWw6Ly9wb3N0Z3Jlczpwb3N0Z3Jlc0Bwb3N0Z3Jlcy1kYi5oZ3J6LWRvZG8tYXBwLW9lbS5zdmMuY2x1c3Rlci5sb2NhbC9wb3N0Z3JlcyIsIkRPRE9fUE9SVF9XRUI9ODA4MCJdfSx7ImJpbiI6Ii91c3IvYmluL2Rlbm8iLCJhcmdzIjpbInRhc2siLCJzdGFydCJdLCJlbnYiOlsiRE9ET19QT1NUR1JFU1FMX0RCX1VSTD1wb3N0Z3Jlc3FsOi8vcG9zdGdyZXM6cG9zdGdyZXNAcG9zdGdyZXMtZGIuaGdyei1kb2RvLWFwcC1vZW0uc3ZjLmNsdXN0ZXIubG9jYWwvcG9zdGdyZXMiLCJET0RPX1BPUlRfV0VCPTgwODAiXX1d",
+						"managerAddr": "",
+						"volumes": []
+					}
+				},
+				"blog-0-ingress": {
+					"chart": {
+						"name": "ingress",
+						"kind": "GitRepository",
+						"address": "https://code.v1.dodo.cloud/helm-charts",
+						"branch": "main",
+						"path": "charts/ingress"
+					},
+					"id": "s6SF2Y9VxueisZ4sGyEp4evD5mGcOTg3Aqwygl3+bVA=",
+					"name": "blog-0-ingress",
+					"dependsOn": [],
+					"cluster": null,
+					"info": "Generating TLS certificate for https://t2.p.v1.dodo.cloud",
+					"annotations": {
+						"dodo.cloud/resource-type": "ingress",
+						"dodo.cloud/resource.ingress.host": "https://t2.p.v1.dodo.cloud",
+						"dodo.cloud/resource.ingress.home": "",
+						"dodo.cloud/resource.ingress.name": "blog"
+					},
+					"values": {
+						"domain": "t2.p.v1.dodo.cloud",
+						"appRoot": "",
+						"ingressClassName": "hgrz-ingress-private",
+						"certificateIssuer": "",
+						"service": {
+							"name": "blog-app",
+							"port": {
+								"number": 8080
+							}
+						}
+					}
+				}
+			}
+		}
+	},
+	"out": {
+		"id": "gP/o6bUhK8yahxnHVq/ARX8rZ06UYvXozT4T5zJFrR4=",
+		"ingress": {},
+		"vm": {},
+		"charts": {
+			"postgres": {
+				"name": "postgres",
+				"kind": "GitRepository",
+				"address": "https://code.v1.dodo.cloud/helm-charts",
+				"branch": "main",
+				"path": "charts/postgresql"
+			},
+			"volume": {
+				"name": "volume",
+				"kind": "GitRepository",
+				"address": "https://code.v1.dodo.cloud/helm-charts",
+				"branch": "main",
+				"path": "charts/volumes"
+			},
+			"secret": {
+				"name": "secret",
+				"kind": "GitRepository",
+				"address": "https://code.v1.dodo.cloud/helm-charts",
+				"branch": "main",
+				"path": "charts/secret"
+			}
+		},
+		"volume": {},
+		"postgresql": {
+			"db": {
+				"id": "t9W0Ivmbsoezk8/UrKXD6yLW7TLOCJLonJBUF0kFKiA=",
+				"name": "db",
+				"version": "15.3",
+				"initSQL": "",
+				"size": "1Gi",
+				"expose": [],
+				"ingress": {},
+				"mongodb": {},
+				"postgresql": {},
+				"vm": {},
+				"charts": {
+					"postgres": {
+						"name": "postgres",
+						"kind": "GitRepository",
+						"address": "https://code.v1.dodo.cloud/helm-charts",
+						"branch": "main",
+						"path": "charts/postgresql"
+					},
+					"volume": {
+						"name": "volume",
+						"kind": "GitRepository",
+						"address": "https://code.v1.dodo.cloud/helm-charts",
+						"branch": "main",
+						"path": "charts/volumes"
+					},
+					"secret": {
+						"name": "secret",
+						"kind": "GitRepository",
+						"address": "https://code.v1.dodo.cloud/helm-charts",
+						"branch": "main",
+						"path": "charts/secret"
+					}
+				},
+				"volume": {
+					"db-postgresql": {
+						"name": "db-postgresql",
+						"size": "1Gi",
+						"accessMode": "ReadWriteOnce"
+					}
+				},
+				"helm": {
+					"postgres-db": {
+						"chart": {
+							"name": "postgres",
+							"kind": "GitRepository",
+							"address": "https://code.v1.dodo.cloud/helm-charts",
+							"branch": "main",
+							"path": "charts/postgresql"
+						},
+						"name": "postgres-db",
+						"dependsOn": [],
+						"info": "",
+						"annotations": {
+							"dodo.cloud/resource-type": "postgresql",
+							"dodo.cloud/resource.postgresql.name": "db",
+							"dodo.cloud/resource.postgresql.version": "15.3",
+							"dodo.cloud/resource.postgresql.volume": "db-postgresql"
+						},
+						"values": {
+							"fullnameOverride": "postgres-db",
+							"image": {
+								"registry": "docker.io",
+								"repository": "library/postgres",
+								"tag": "15.3",
+								"pullPolicy": "IfNotPresent"
+							},
+							"auth": {
+								"postgresPassword": "postgres",
+								"username": "postgres",
+								"password": "postgres",
+								"database": "postgres"
+							},
+							"service": {
+								"type": "ClusterIP",
+								"port": 5432
+							},
+							"global": {
+								"postgresql": {
+									"auth": {
+										"postgresPassword": "postgres",
+										"username": "postgres",
+										"password": "postgres",
+										"database": "postgres"
+									}
+								}
+							},
+							"primary": {
+								"persistence": {
+									"existingClaim": "db-postgresql"
+								},
+								"securityContext": {
+									"enabled": true,
+									"fsGroup": 0
+								},
+								"containerSecurityContext": {
+									"enabled": true,
+									"runAsUser": 0
+								}
+							},
+							"volumePermissions": {
+								"securityContext": {
+									"runAsUser": 0
+								}
+							}
+						}
+					},
+					"db-postgresql-volume": {
+						"chart": {
+							"name": "volume",
+							"kind": "GitRepository",
+							"address": "https://code.v1.dodo.cloud/helm-charts",
+							"branch": "main",
+							"path": "charts/volumes"
+						},
+						"name": "db-postgresql-volume",
+						"dependsOn": [],
+						"info": "Creating disk for db-postgresql",
+						"annotations": {
+							"dodo.cloud/resource-type": "volume",
+							"dodo.cloud/resource.volume.name": "db-postgresql",
+							"dodo.cloud/resource.volume.size": "1Gi"
+						},
+						"values": {
+							"name": "db-postgresql",
+							"size": "1Gi",
+							"accessMode": "ReadWriteOnce"
+						}
+					}
+				},
+				"openPort": [],
+				"openPortMap": {
+					"_self": []
+				},
+				"clusterProxy": {},
+				"images": {
+					"postgres": {
+						"registry": "docker.io",
+						"repository": "library",
+						"name": "postgres",
+						"tag": "15.3",
+						"pullPolicy": "IfNotPresent",
+						"imageName": "library/postgres",
+						"fullName": "docker.io/library/postgres",
+						"fullNameWithTag": "docker.io/library/postgres:15.3"
+					}
+				},
+				"helmR": {
+					"postgres-db": {
+						"chart": {
+							"name": "postgres",
+							"kind": "GitRepository",
+							"address": "https://code.v1.dodo.cloud/helm-charts",
+							"branch": "main",
+							"path": "charts/postgresql"
+						},
+						"id": "t9W0Ivmbsoezk8/UrKXD6yLW7TLOCJLonJBUF0kFKiA=",
+						"name": "postgres-db",
+						"dependsOn": [],
+						"info": "",
+						"annotations": {
+							"dodo.cloud/resource-type": "postgresql",
+							"dodo.cloud/resource.postgresql.name": "db",
+							"dodo.cloud/resource.postgresql.version": "15.3",
+							"dodo.cloud/resource.postgresql.volume": "db-postgresql"
+						},
+						"values": {
+							"fullnameOverride": "postgres-db",
+							"image": {
+								"registry": "docker.io",
+								"repository": "library/postgres",
+								"tag": "15.3",
+								"pullPolicy": "IfNotPresent"
+							},
+							"auth": {
+								"postgresPassword": "postgres",
+								"username": "postgres",
+								"password": "postgres",
+								"database": "postgres"
+							},
+							"service": {
+								"type": "ClusterIP",
+								"port": 5432
+							},
+							"global": {
+								"postgresql": {
+									"auth": {
+										"postgresPassword": "postgres",
+										"username": "postgres",
+										"password": "postgres",
+										"database": "postgres"
+									}
+								}
+							},
+							"primary": {
+								"persistence": {
+									"existingClaim": "db-postgresql"
+								},
+								"securityContext": {
+									"enabled": true,
+									"fsGroup": 0
+								},
+								"containerSecurityContext": {
+									"enabled": true,
+									"runAsUser": 0
+								}
+							},
+							"volumePermissions": {
+								"securityContext": {
+									"runAsUser": 0
+								}
+							}
+						}
+					},
+					"db-postgresql-volume": {
+						"chart": {
+							"name": "volume",
+							"kind": "GitRepository",
+							"address": "https://code.v1.dodo.cloud/helm-charts",
+							"branch": "main",
+							"path": "charts/volumes"
+						},
+						"id": "t9W0Ivmbsoezk8/UrKXD6yLW7TLOCJLonJBUF0kFKiA=",
+						"name": "db-postgresql-volume",
+						"dependsOn": [],
+						"info": "Creating disk for db-postgresql",
+						"annotations": {
+							"dodo.cloud/resource-type": "volume",
+							"dodo.cloud/resource.volume.name": "db-postgresql",
+							"dodo.cloud/resource.volume.size": "1Gi"
+						},
+						"values": {
+							"name": "db-postgresql",
+							"size": "1Gi",
+							"accessMode": "ReadWriteOnce"
+						}
+					}
+				}
+			}
+		},
+		"mongodb": {},
+		"openPortMap": {
+			"_self": [],
+			"postgresql-db": {
+				"_self": []
+			}
+		},
+		"clusterProxy": {},
+		"images": {
+			"postgres": {
+				"registry": "docker.io",
+				"repository": "library",
+				"name": "postgres",
+				"tag": "15.3",
+				"pullPolicy": "IfNotPresent",
+				"imageName": "library/postgres",
+				"fullName": "docker.io/library/postgres",
+				"fullNameWithTag": "docker.io/library/postgres:15.3"
+			}
+		},
+		"helm": {},
+		"openPort": [],
+		"helmR": {
+			"postgres-db": {
+				"chart": {
+					"name": "postgres",
+					"kind": "GitRepository",
+					"address": "https://code.v1.dodo.cloud/helm-charts",
+					"branch": "main",
+					"path": "charts/postgresql"
+				},
+				"id": "t9W0Ivmbsoezk8/UrKXD6yLW7TLOCJLonJBUF0kFKiA=",
+				"name": "postgres-db",
+				"dependsOn": [],
+				"info": "",
+				"annotations": {
+					"dodo.cloud/resource-type": "postgresql",
+					"dodo.cloud/resource.postgresql.name": "db",
+					"dodo.cloud/resource.postgresql.version": "15.3",
+					"dodo.cloud/resource.postgresql.volume": "db-postgresql"
+				},
+				"values": {
+					"fullnameOverride": "postgres-db",
+					"image": {
+						"registry": "docker.io",
+						"repository": "library/postgres",
+						"tag": "15.3",
+						"pullPolicy": "IfNotPresent"
+					},
+					"auth": {
+						"postgresPassword": "postgres",
+						"username": "postgres",
+						"password": "postgres",
+						"database": "postgres"
+					},
+					"service": {
+						"type": "ClusterIP",
+						"port": 5432
+					},
+					"global": {
+						"postgresql": {
+							"auth": {
+								"postgresPassword": "postgres",
+								"username": "postgres",
+								"password": "postgres",
+								"database": "postgres"
+							}
+						}
+					},
+					"primary": {
+						"persistence": {
+							"existingClaim": "db-postgresql"
+						},
+						"securityContext": {
+							"enabled": true,
+							"fsGroup": 0
+						},
+						"containerSecurityContext": {
+							"enabled": true,
+							"runAsUser": 0
+						}
+					},
+					"volumePermissions": {
+						"securityContext": {
+							"runAsUser": 0
+						}
+					}
+				}
+			},
+			"db-postgresql-volume": {
+				"chart": {
+					"name": "volume",
+					"kind": "GitRepository",
+					"address": "https://code.v1.dodo.cloud/helm-charts",
+					"branch": "main",
+					"path": "charts/volumes"
+				},
+				"id": "t9W0Ivmbsoezk8/UrKXD6yLW7TLOCJLonJBUF0kFKiA=",
+				"name": "db-postgresql-volume",
+				"dependsOn": [],
+				"info": "Creating disk for db-postgresql",
+				"annotations": {
+					"dodo.cloud/resource-type": "volume",
+					"dodo.cloud/resource.volume.name": "db-postgresql",
+					"dodo.cloud/resource.volume.size": "1Gi"
+				},
+				"values": {
+					"name": "db-postgresql",
+					"size": "1Gi",
+					"accessMode": "ReadWriteOnce"
+				}
+			}
+		}
+	},
+	"resources": {}
+}
+`
+
+func TestDecode(t *testing.T) {
+	if outs, err := DecodeResourceOuts([]byte(contents)); err != nil {
+		t.Fatal(err)
+	} else {
+		t.Logf("%+v\n", outs)
+	}
+}
diff --git a/core/installer/status/status.go b/core/installer/status/status.go
index ebec72f..f093a93 100644
--- a/core/installer/status/status.go
+++ b/core/installer/status/status.go
@@ -45,6 +45,7 @@
 )
 
 type ResourceRef struct {
+	Id        string
 	Name      string
 	Namespace string
 }
diff --git a/core/installer/tasks/release.go b/core/installer/tasks/release.go
index 1bd0d6e..dfec990 100644
--- a/core/installer/tasks/release.go
+++ b/core/installer/tasks/release.go
@@ -38,6 +38,7 @@
 	}
 	t := newResourceLeafTask(h.Info, &ResourceId{rType, name}, func() error {
 		ref := status.ResourceRef{
+			Id:        h.Annotations["dodo.cloud/id"],
 			Name:      h.Name,
 			Namespace: h.Namespace,
 		}