Repository: argoproj/argo-cd
Pull request #11401 (2022-11-22 15:55:03)

feat: added deep links backend changes
Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>

Related https://github.com/argoproj/argo-cd/issues/4281

This PR implements the backend changes for Deep Links [proposal](https://github.com/argoproj/argo-cd/blob/master/docs/proposals/deep-links.md)

Checklist:

* [ ] Either (a) I've created an [enhancement proposal](https://github.com/argoproj/argo-cd/issues/new/choose) and discussed it with the community, (b) this is a bug fix, or (c) this does not need to be in the release notes.
* [x] The title of the PR states what changed and the related issues number (used for the release note).
* [ ] I've included "Closes [ISSUE #]" or "Fixes [ISSUE #]" in the description to automatically close the associated issue.
* [ ] I've updated both the CLI and UI to expose my feature, or I plan to submit a second PR with them.
* [ ] Does this PR require documentation updates?
* [ ] I've updated documentation as required by this PR.
* [ ] Optional. My organization is added to USERS.md.
* [x] I have signed off all my commits as required by [DCO](https://github.com/argoproj/argoproj/tree/master/community#contributing-to-argo)
* [x] I have written unit and/or e2e tests for my change. PRs without these are unlikely to be merged.
* [ ] My build is green ([troubleshooting builds](https://argo-cd.readthedocs.io/en/latest/developer-guide/ci/)). 



codecov[bot]: # [Codecov](https://codecov.io/gh/argoproj/argo-cd/pull/11401?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj) Report
Base: **46.67**% // Head: **46.64**% // Decreases project coverage by **`-0.03%`** :warning:
> Coverage data is based on head [(`1e9de0d`)](https://codecov.io/gh/argoproj/argo-cd/pull/11401?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj) compared to base [(`437cd4f`)](https://codecov.io/gh/argoproj/argo-cd/commit/437cd4f1b5a9b276596919b1803d398fc9c13a81?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj).
> Patch coverage: 35.18% of modified lines in pull request are covered.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master   #11401      +/-   ##
==========================================
- Coverage   46.67%   46.64%   -0.04%     
==========================================
  Files         239      240       +1     
  Lines       39631    39738     +107     
==========================================
+ Hits        18497    18534      +37     
- Misses      19269    19337      +68     
- Partials     1865     1867       +2     
```


| [Impacted Files](https://codecov.io/gh/argoproj/argo-cd/pull/11401?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj) | Coverage Δ | |
|---|---|---|
| [server/application/application.go](https://codecov.io/gh/argoproj/argo-cd/pull/11401/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj#diff-c2VydmVyL2FwcGxpY2F0aW9uL2FwcGxpY2F0aW9uLmdv) | `28.09% <0.00%> (-0.53%)` | :arrow_down: |
| [server/project/project.go](https://codecov.io/gh/argoproj/argo-cd/pull/11401/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj#diff-c2VydmVyL3Byb2plY3QvcHJvamVjdC5nbw==) | `51.23% <0.00%> (-2.39%)` | :arrow_down: |
| [util/settings/settings.go](https://codecov.io/gh/argoproj/argo-cd/pull/11401/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj#diff-dXRpbC9zZXR0aW5ncy9zZXR0aW5ncy5nbw==) | `48.38% <0.00%> (-0.49%)` | :arrow_down: |
| [server/deeplinks/deeplinks.go](https://codecov.io/gh/argoproj/argo-cd/pull/11401/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj#diff-c2VydmVyL2RlZXBsaW5rcy9kZWVwbGlua3MuZ28=) | `85.71% <85.71%> (ø)` | |
| [server/server.go](https://codecov.io/gh/argoproj/argo-cd/pull/11401/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj#diff-c2VydmVyL3NlcnZlci5nbw==) | `53.62% <100.00%> (+0.05%)` | :arrow_up: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj)

</details>

[:umbrella: View full report at Codecov](https://codecov.io/gh/argoproj/argo-cd/pull/11401?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj).


Patch:
diff --git a/assets/swagger.json b/assets/swagger.json
index 4d783849e..c48dbb3cd 100644
--- a/assets/swagger.json
+++ b/assets/swagger.json
@@ -735,6 +735,42 @@
         }
       }
     },
+    "/api/v1/applications/{name}/links": {
+      "get": {
+        "tags": [
+          "ApplicationService"
+        ],
+        "summary": "ListLinks returns the list of all application deep links",
+        "operationId": "ApplicationService_ListLinks",
+        "parameters": [
+          {
+            "type": "string",
+            "name": "name",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "name": "namespace",
+            "in": "query"
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/applicationLinksResponse"
+            }
+          },
+          "default": {
+            "description": "An unexpected error response.",
+            "schema": {
+              "$ref": "#/definitions/runtimeError"
+            }
+          }
+        }
+      }
+    },
     "/api/v1/applications/{name}/logs": {
       "get": {
         "tags": [
@@ -1385,6 +1421,67 @@
         }
       }
     },
+    "/api/v1/applications/{name}/resource/links": {
+      "get": {
+        "tags": [
+          "ApplicationService"
+        ],
+        "summary": "ListResourceLinks returns the list of all resource deep links",
+        "operationId": "ApplicationService_ListResourceLinks",
+        "parameters": [
+          {
+            "type": "string",
+            "name": "name",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "name": "namespace",
+            "in": "query"
+          },
+          {
+            "type": "string",
+            "name": "resourceName",
+            "in": "query"
+          },
+          {
+            "type": "string",
+            "name": "version",
+            "in": "query"
+          },
+          {
+            "type": "string",
+            "name": "group",
+            "in": "query"
+          },
+          {
+            "type": "string",
+            "name": "kind",
+            "in": "query"
+          },
+          {
+            "type": "string",
+            "name": "appNamespace",
+            "in": "query"
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/applicationLinksResponse"
+            }
+          },
+          "default": {
+            "description": "An unexpected error response.",
+            "schema": {
+              "$ref": "#/definitions/runtimeError"
+            }
+          }
+        }
+      }
+    },
     "/api/v1/applications/{name}/revisions/{revision}/metadata": {
       "get": {
         "tags": [
@@ -2560,6 +2657,37 @@
         }
       }
     },
+    "/api/v1/projects/{name}/links": {
+      "get": {
+        "tags": [
+          "ProjectService"
+        ],
+        "summary": "ListLinks returns all deep links for the particular project",
+        "operationId": "ProjectService_ListLinks",
+        "parameters": [
+          {
+            "type": "string",
+            "name": "name",
+            "in": "path",
+            "required": true
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/applicationLinksResponse"
+            }
+          },
+          "default": {
+            "description": "An unexpected error response.",
+            "schema": {
+              "$ref": "#/definitions/runtimeError"
+            }
+          }
+        }
+      }
+    },
     "/api/v1/projects/{name}/syncwindows": {
       "get": {
         "tags": [
@@ -3900,6 +4028,34 @@
         }
       }
     },
+    "applicationLinkInfo": {
+      "type": "object",
+      "properties": {
+        "description": {
+          "type": "string"
+        },
+        "iconClass": {
+          "type": "string"
+        },
+        "title": {
+          "type": "string"
+        },
+        "url": {
+          "type": "string"
+        }
+      }
+    },
+    "applicationLinksResponse": {
+      "type": "object",
+      "properties": {
+        "items": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/applicationLinkInfo"
+          }
+        }
+      }
+    },
     "applicationLogEntry": {
       "type": "object",
       "properties": {
diff --git a/docs/operator-manual/argocd-cm.yaml b/docs/operator-manual/argocd-cm.yaml
index 5715f3a5d..851738380 100644
--- a/docs/operator-manual/argocd-cm.yaml
+++ b/docs/operator-manual/argocd-cm.yaml
@@ -305,3 +305,29 @@ data:
   # token verification to pass despite the OIDC provider having an invalid certificate. Only set to "true" if you
   # understand the risks.
   oidc.tls.insecure.skip.verify: "false"
+
+  # Add Deep Links to ArgoCD UI
+  # sample project level links
+  project.links: |
+    - url: https://myaudit-system.com?project={{.metadata.name}}
+      title: Audit
+      description: system audit logs
+      icon.class: "fa-book"
+  # sample application level links
+  application.links: |
+    # pkg.go.dev/text/template is used for evaluating url templates
+    - url: https://mycompany.splunk.com?search={{.spec.destination.namespace}}
+      title: Splunk
+    # conditionally show link e.g. for specific project
+    # github.com/antonmedv/expr is used for evaluation of conditions
+    - url: https://mycompany.splunk.com?search={{.spec.destination.namespace}}
+      title: Splunk
+      if: spec.project == "default"
+    - url: https://{{.metadata.annotations.splunkhost}}?search={{.spec.destination.namespace}}
+      title: Splunk
+      if: metadata.annotations.splunkhost
+  # sample resource level links
+  resource.links: |
+    - url: https://mycompany.splunk.com?search={{.metadata.namespace}}
+      title: Splunk
+      if: kind == "Pod" || kind == "Deployment"
\ No newline at end of file
diff --git a/go.mod b/go.mod
index 24d25ae7e..f41a2a3ac 100644
--- a/go.mod
+++ b/go.mod
@@ -23,9 +23,9 @@ require (
 	github.com/ghodss/yaml v1.0.0
 	github.com/go-git/go-git/v5 v5.4.2
 	github.com/go-logr/logr v1.2.3
-	github.com/go-openapi/loads v0.21.1
+	github.com/go-openapi/loads v0.21.2
 	github.com/go-openapi/runtime v0.25.0
-	github.com/go-openapi/spec v0.20.4 // indirect
+	github.com/go-openapi/spec v0.20.6 // indirect
 	github.com/go-openapi/validate v0.21.0 // indirect
 	github.com/go-redis/cache/v8 v8.4.2
 	github.com/go-redis/redis/v8 v8.11.3
@@ -93,7 +93,7 @@ require (
 	k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
 	layeh.com/gopher-json v0.0.0-20190114024228-97fed8db8427
 	sigs.k8s.io/controller-runtime v0.11.0
-	sigs.k8s.io/structured-merge-diff/v4 v4.2.1
+	sigs.k8s.io/structured-merge-diff/v4 v4.2.3
 	sigs.k8s.io/yaml v1.3.0
 )
 
@@ -106,6 +106,7 @@ require (
 
 require (
 	github.com/Masterminds/sprig/v3 v3.2.2
+	github.com/antonmedv/expr v1.9.0
 	github.com/gosimple/slug v1.13.1
 	github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5
 	github.com/robfig/cron/v3 v3.0.1
@@ -139,12 +140,9 @@ require (
 	github.com/Microsoft/go-winio v0.4.17 // indirect
 	github.com/PagerDuty/go-pagerduty v1.6.0 // indirect
 	github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
-	github.com/PuerkitoBio/purell v1.1.1 // indirect
-	github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
 	github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20210112200207-10ab4d695d60 // indirect
 	github.com/acomagu/bufpipe v1.0.3 // indirect
 	github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect
-	github.com/antonmedv/expr v1.9.0 // indirect
 	github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/cenkalti/backoff/v4 v4.1.3 // indirect
@@ -163,13 +161,12 @@ require (
 	github.com/go-git/gcfg v1.5.0 // indirect
 	github.com/go-git/go-billy/v5 v5.3.1 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
-	github.com/go-openapi/analysis v0.21.2 // indirect
+	github.com/go-openapi/analysis v0.21.4 // indirect
 	github.com/go-openapi/errors v0.20.2 // indirect
 	github.com/go-openapi/jsonpointer v0.19.5 // indirect
-	github.com/go-openapi/jsonreference v0.19.6 // indirect
-	github.com/go-openapi/strfmt v0.21.2 // indirect
+	github.com/go-openapi/jsonreference v0.20.0 // indirect
+	github.com/go-openapi/strfmt v0.21.3 // indirect
 	github.com/go-openapi/swag v0.21.1 // indirect
-	github.com/go-stack/stack v1.8.1 // indirect
 	github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 // indirect
 	github.com/golang/glog v1.0.0 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
@@ -226,7 +223,7 @@ require (
 	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
 	github.com/xanzy/ssh-agent v0.3.0 // indirect
 	github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect
-	go.mongodb.org/mongo-driver v1.8.3 // indirect
+	go.mongodb.org/mongo-driver v1.10.0 // indirect
 	go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.1 // indirect
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.1 // indirect
 	go.opentelemetry.io/otel/trace v1.11.1 // indirect
diff --git a/go.sum b/go.sum
index af581ffd1..9e8e1a84b 100644
--- a/go.sum
+++ b/go.sum
@@ -105,9 +105,7 @@ github.com/PagerDuty/go-pagerduty v1.6.0 h1:am81SzvG5Pw+s3JZ5yEy6kGvsXXklTNRrGr3
 github.com/PagerDuty/go-pagerduty v1.6.0/go.mod h1:7eaBLzsDpK7VUvU0SJ5mohczQkoWrrr5CjDaw5gh1as=
 github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ=
 github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
-github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
 github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
 github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20210112200207-10ab4d695d60 h1:prBTRx78AQnXzivNT9Crhu564W/zPPr3ibSlpT9xKcE=
 github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20210112200207-10ab4d695d60/go.mod h1:rjP7sIipbZcagro/6TCk6X0ZeFT2eyudH5+fve/cbBA=
@@ -382,8 +380,9 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/go-logr/zapr v1.2.0 h1:n4JnPI1T3Qq1SFEi/F8rwLrZERp2bso19PJZDB9dayk=
 github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro=
-github.com/go-openapi/analysis v0.21.2 h1:hXFrOYFHUAMQdu6zwAiKKJHJQ8kqZs1ux/ru1P1wLJU=
 github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY=
+github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc=
+github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo=
 github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
 github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
 github.com/go-openapi/errors v0.20.2 h1:dxy7PGTqEh94zj2E3h1cUmQQWiM1+aeCROfAr02EmK8=
@@ -393,18 +392,21 @@ github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUe
 github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
 github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
 github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
-github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
 github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
-github.com/go-openapi/loads v0.21.1 h1:Wb3nVZpdEzDTcly8S4HMkey6fjARRzb7iEaySimlDW0=
+github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA=
+github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
 github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g=
+github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro=
+github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw=
 github.com/go-openapi/runtime v0.25.0 h1:7yQTCdRbWhX8vnIjdzU8S00tBYf7Sg71EBeorlPHvhc=
 github.com/go-openapi/runtime v0.25.0/go.mod h1:Ux6fikcHXyyob6LNWxtE96hWwjBPYF0DXgVFuMTneOs=
-github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
 github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
+github.com/go-openapi/spec v0.20.6 h1:ich1RQ3WDbfoeTqTAb+5EIxNmpKVJZWBNah9RAT0jIQ=
+github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
 github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg=
 github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k=
-github.com/go-openapi/strfmt v0.21.2 h1:5NDNgadiX1Vhemth/TH4gCGopWSTdDjxl60H3B7f+os=
-github.com/go-openapi/strfmt v0.21.2/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k=
+github.com/go-openapi/strfmt v0.21.3 h1:xwhj5X6CjXEZZHMWy1zKJxvW9AfHC9pkyUjLvHtKG7o=
+github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg=
 github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
 github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
 github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
@@ -421,8 +423,6 @@ github.com/go-redis/redis/v8 v8.11.3 h1:GCjoYp8c+yQTJfc0n69iwSiHjvuAdruxl7elnZCx
 github.com/go-redis/redis/v8 v8.11.3/go.mod h1:xNJ9xDG09FsIPwh3bWdk+0oDWHbtF9rPN0F/oD9XeKc=
 github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
-github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
 github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
 github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
 github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
@@ -1102,7 +1102,9 @@ github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI
 github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
 github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
 github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
+github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
 github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
+github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
 github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
 github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
@@ -1132,8 +1134,8 @@ go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD0
 go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4=
 go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg=
 go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng=
-go.mongodb.org/mongo-driver v1.8.3 h1:TDKlTkGDKm9kkJVUOAXDK5/fkqKHJVwYQSpoRfB43R4=
-go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=
+go.mongodb.org/mongo-driver v1.10.0 h1:UtV6N5k14upNp4LTduX0QCufG124fSu25Wz9tu94GLg=
+go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8=
 go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
 go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@@ -1212,12 +1214,12 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh
 golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1479,7 +1481,6 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
 golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -1887,8 +1888,9 @@ sigs.k8s.io/kustomize/kustomize/v4 v4.5.4/go.mod h1:Zo/Xc5FKD6sHl0lilbrieeGeZHVY
 sigs.k8s.io/kustomize/kyaml v0.13.6 h1:eF+wsn4J7GOAXlvajv6OknSunxpcOBQQqsnPxObtkGs=
 sigs.k8s.io/kustomize/kyaml v0.13.6/go.mod h1:yHP031rn1QX1lr/Xd934Ri/xdVNG8BE2ECa78Ht/kEg=
 sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
-sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y=
 sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
+sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
+sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
 sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
 sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
 sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
diff --git a/hack/generate-proto.sh b/hack/generate-proto.sh
index cdd3cf283..d544bd045 100755
--- a/hack/generate-proto.sh
+++ b/hack/generate-proto.sh
@@ -125,7 +125,7 @@ clean_swagger() {
 }
 
 echo "If additional types are added, the number of expected collisions may need to be increased"
-EXPECTED_COLLISION_COUNT=93
+EXPECTED_COLLISION_COUNT=95
 collect_swagger server ${EXPECTED_COLLISION_COUNT}
 clean_swagger server
 clean_swagger reposerver
diff --git a/pkg/apiclient/application/application.pb.go b/pkg/apiclient/application/application.pb.go
index 60f4b7aa4..0de79ae03 100644
--- a/pkg/apiclient/application/application.pb.go
+++ b/pkg/apiclient/application/application.pb.go
@@ -2411,6 +2411,179 @@ func (m *ManagedResourcesResponse) GetItems() []*v1alpha1.ResourceDiff {
 	return nil
 }
 
+type LinkInfo struct {
+	Title                *string  `protobuf:"bytes,1,req,name=title" json:"title,omitempty"`
+	Url                  *string  `protobuf:"bytes,2,req,name=url" json:"url,omitempty"`
+	Description          *string  `protobuf:"bytes,3,opt,name=description" json:"description,omitempty"`
+	IconClass            *string  `protobuf:"bytes,4,opt,name=iconClass" json:"iconClass,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *LinkInfo) Reset()         { *m = LinkInfo{} }
+func (m *LinkInfo) String() string { return proto.CompactTextString(m) }
+func (*LinkInfo) ProtoMessage()    {}
+func (*LinkInfo) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df6e82b174b5eaec, []int{32}
+}
+func (m *LinkInfo) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *LinkInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_LinkInfo.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *LinkInfo) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_LinkInfo.Merge(m, src)
+}
+func (m *LinkInfo) XXX_Size() int {
+	return m.Size()
+}
+func (m *LinkInfo) XXX_DiscardUnknown() {
+	xxx_messageInfo_LinkInfo.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_LinkInfo proto.InternalMessageInfo
+
+func (m *LinkInfo) GetTitle() string {
+	if m != nil && m.Title != nil {
+		return *m.Title
+	}
+	return ""
+}
+
+func (m *LinkInfo) GetUrl() string {
+	if m != nil && m.Url != nil {
+		return *m.Url
+	}
+	return ""
+}
+
+func (m *LinkInfo) GetDescription() string {
+	if m != nil && m.Description != nil {
+		return *m.Description
+	}
+	return ""
+}
+
+func (m *LinkInfo) GetIconClass() string {
+	if m != nil && m.IconClass != nil {
+		return *m.IconClass
+	}
+	return ""
+}
+
+type LinksResponse struct {
+	Items                []*LinkInfo `protobuf:"bytes,1,rep,name=items" json:"items,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
+	XXX_unrecognized     []byte      `json:"-"`
+	XXX_sizecache        int32       `json:"-"`
+}
+
+func (m *LinksResponse) Reset()         { *m = LinksResponse{} }
+func (m *LinksResponse) String() string { return proto.CompactTextString(m) }
+func (*LinksResponse) ProtoMessage()    {}
+func (*LinksResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df6e82b174b5eaec, []int{33}
+}
+func (m *LinksResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *LinksResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_LinksResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *LinksResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_LinksResponse.Merge(m, src)
+}
+func (m *LinksResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *LinksResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_LinksResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_LinksResponse proto.InternalMessageInfo
+
+func (m *LinksResponse) GetItems() []*LinkInfo {
+	if m != nil {
+		return m.Items
+	}
+	return nil
+}
+
+type ListAppLinksRequest struct {
+	Name                 *string  `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
+	Namespace            *string  `protobuf:"bytes,3,opt,name=namespace" json:"namespace,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *ListAppLinksRequest) Reset()         { *m = ListAppLinksRequest{} }
+func (m *ListAppLinksRequest) String() string { return proto.CompactTextString(m) }
+func (*ListAppLinksRequest) ProtoMessage()    {}
+func (*ListAppLinksRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df6e82b174b5eaec, []int{34}
+}
+func (m *ListAppLinksRequest) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *ListAppLinksRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_ListAppLinksRequest.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *ListAppLinksRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ListAppLinksRequest.Merge(m, src)
+}
+func (m *ListAppLinksRequest) XXX_Size() int {
+	return m.Size()
+}
+func (m *ListAppLinksRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_ListAppLinksRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ListAppLinksRequest proto.InternalMessageInfo
+
+func (m *ListAppLinksRequest) GetName() string {
+	if m != nil && m.Name != nil {
+		return *m.Name
+	}
+	return ""
+}
+
+func (m *ListAppLinksRequest) GetNamespace() string {
+	if m != nil && m.Namespace != nil {
+		return *m.Namespace
+	}
+	return ""
+}
+
 func init() {
 	proto.RegisterType((*ApplicationQuery)(nil), "application.ApplicationQuery")
 	proto.RegisterType((*NodeQuery)(nil), "application.NodeQuery")
@@ -2444,6 +2617,9 @@ func init() {
 	proto.RegisterType((*OperationTerminateResponse)(nil), "application.OperationTerminateResponse")
 	proto.RegisterType((*ResourcesQuery)(nil), "application.ResourcesQuery")
 	proto.RegisterType((*ManagedResourcesResponse)(nil), "application.ManagedResourcesResponse")
+	proto.RegisterType((*LinkInfo)(nil), "application.LinkInfo")
+	proto.RegisterType((*LinksResponse)(nil), "application.LinksResponse")
+	proto.RegisterType((*ListAppLinksRequest)(nil), "application.ListAppLinksRequest")
 }
 
 func init() {
@@ -2451,160 +2627,169 @@ func init() {
 }
 
 var fileDescriptor_df6e82b174b5eaec = []byte{
-	// 2444 bytes of a gzipped FileDescriptorProto
+	// 2581 bytes of a gzipped FileDescriptorProto
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0xcd, 0x8f, 0x1c, 0x47,
-	0x15, 0xa7, 0x66, 0xbf, 0x66, 0xde, 0xac, 0xbf, 0x2a, 0xf1, 0xd2, 0x19, 0xaf, 0xcd, 0xa6, 0xfd,
-	0xb5, 0x5e, 0x7b, 0x67, 0xec, 0xc1, 0x20, 0x67, 0x03, 0x02, 0xdb, 0xf1, 0x17, 0xac, 0x1d, 0xd3,
-	0x6b, 0xb3, 0x28, 0x1c, 0xa0, 0xd2, 0x5d, 0x3b, 0xdb, 0xec, 0x4c, 0x57, 0xbb, 0xbb, 0x67, 0xac,
-	0x91, 0xf1, 0x25, 0x08, 0x4e, 0x28, 0x48, 0x49, 0x0e, 0x28, 0x8a, 0x10, 0x4a, 0x94, 0x0b, 0x17,
-	0x6e, 0x08, 0x89, 0x0b, 0x5c, 0x10, 0x48, 0x1c, 0x10, 0x1f, 0x97, 0x9c, 0x90, 0xc5, 0x8d, 0x0b,
-	0x7f, 0x02, 0xaa, 0xea, 0xaa, 0xee, 0xea, 0x99, 0x9e, 0x9e, 0x59, 0x76, 0xa3, 0xf8, 0x56, 0xaf,
-	0xba, 0xea, 0xbd, 0x5f, 0xbd, 0x7a, 0xef, 0x55, 0xd5, 0x6f, 0x06, 0x4e, 0x85, 0x34, 0xe8, 0xd1,
-	0xa0, 0x41, 0x7c, 0xbf, 0xed, 0xda, 0x24, 0x72, 0x99, 0xa7, 0xb7, 0xeb, 0x7e, 0xc0, 0x22, 0x86,
-	0xab, 0x5a, 0x57, 0x6d, 0xb1, 0xc5, 0x58, 0xab, 0x4d, 0x1b, 0xc4, 0x77, 0x1b, 0xc4, 0xf3, 0x58,
-	0x24, 0xba, 0xc3, 0x78, 0x68, 0xcd, 0xdc, 0xb9, 0x12, 0xd6, 0x5d, 0x26, 0xbe, 0xda, 0x2c, 0xa0,
-	0x8d, 0xde, 0xa5, 0x46, 0x8b, 0x7a, 0x34, 0x20, 0x11, 0x75, 0xe4, 0x98, 0xcb, 0xe9, 0x98, 0x0e,
-	0xb1, 0xb7, 0x5d, 0x8f, 0x06, 0xfd, 0x86, 0xbf, 0xd3, 0xe2, 0x1d, 0x61, 0xa3, 0x43, 0x23, 0x92,
-	0x37, 0x6b, 0xbd, 0xe5, 0x46, 0xdb, 0xdd, 0x37, 0xeb, 0x36, 0xeb, 0x34, 0x48, 0xd0, 0x62, 0x7e,
-	0xc0, 0x7e, 0x20, 0x1a, 0xab, 0xb6, 0xd3, 0xe8, 0x35, 0x53, 0x05, 0xfa, 0x5a, 0x7a, 0x97, 0x48,
-	0xdb, 0xdf, 0x26, 0xc3, 0xda, 0x6e, 0x8c, 0xd1, 0x16, 0x50, 0x9f, 0x49, 0xdf, 0x88, 0xa6, 0x1b,
-	0xb1, 0xa0, 0xaf, 0x35, 0x63, 0x35, 0xe6, 0x27, 0x08, 0x0e, 0x5f, 0x4d, 0xed, 0x7d, 0xab, 0x4b,
-	0x83, 0x3e, 0xc6, 0x30, 0xed, 0x91, 0x0e, 0x35, 0xd0, 0x12, 0x5a, 0xae, 0x58, 0xa2, 0x8d, 0x0d,
-	0x98, 0x0b, 0xe8, 0x56, 0x40, 0xc3, 0x6d, 0xa3, 0x24, 0xba, 0x95, 0x88, 0x6b, 0x50, 0xe6, 0xc6,
-	0xa9, 0x1d, 0x85, 0xc6, 0xd4, 0xd2, 0xd4, 0x72, 0xc5, 0x4a, 0x64, 0xbc, 0x0c, 0x87, 0x02, 0x1a,
-	0xb2, 0x6e, 0x60, 0xd3, 0x6f, 0xd3, 0x20, 0x74, 0x99, 0x67, 0x4c, 0x8b, 0xd9, 0x83, 0xdd, 0x5c,
-	0x4b, 0x48, 0xdb, 0xd4, 0x8e, 0x58, 0x60, 0xcc, 0x88, 0x21, 0x89, 0xcc, 0xf1, 0x70, 0xe0, 0xc6,
-	0x6c, 0x8c, 0x87, 0xb7, 0xb1, 0x09, 0xf3, 0xc4, 0xf7, 0xef, 0x91, 0x0e, 0x0d, 0x7d, 0x62, 0x53,
-	0x63, 0x4e, 0x7c, 0xcb, 0xf4, 0x99, 0xd7, 0xa1, 0x72, 0x8f, 0x39, 0x74, 0xf4, 0xa2, 0x06, 0x95,
-	0x94, 0x72, 0x94, 0xec, 0xc0, 0x51, 0x8b, 0xf6, 0x5c, 0x0e, 0xf2, 0x2e, 0x8d, 0x88, 0x43, 0x22,
-	0x32, 0xa8, 0xb0, 0x94, 0x28, 0xac, 0x41, 0x39, 0x90, 0x83, 0x8d, 0x92, 0xe8, 0x4f, 0xe4, 0x21,
-	0x63, 0x53, 0x39, 0xc6, 0xfe, 0x82, 0xe0, 0x84, 0xb6, 0x1d, 0x96, 0x74, 0xd2, 0x8d, 0x1e, 0xf5,
-	0xa2, 0x70, 0xb4, 0xd9, 0x0b, 0x70, 0x44, 0xf9, 0x73, 0x70, 0x31, 0xc3, 0x1f, 0x38, 0x10, 0xbd,
-	0x53, 0x01, 0xd1, 0xfb, 0xf0, 0x12, 0x54, 0x95, 0xfc, 0xf0, 0xce, 0x6b, 0x72, 0xd3, 0xf4, 0xae,
-	0xa1, 0xe5, 0xcc, 0xe4, 0x2c, 0xc7, 0x03, 0x43, 0x5b, 0xcd, 0x5d, 0xe2, 0xb9, 0x5b, 0x34, 0x8c,
-	0x26, 0x75, 0x1f, 0xda, 0xb5, 0xfb, 0x5e, 0x86, 0xca, 0x4d, 0xb7, 0x4d, 0xaf, 0x6f, 0x77, 0xbd,
-	0x1d, 0xfc, 0x22, 0xcc, 0xd8, 0xbc, 0x21, 0x2c, 0xcc, 0x5b, 0xb1, 0x60, 0x3e, 0x86, 0x97, 0x47,
-	0x41, 0xda, 0x74, 0xa3, 0x6d, 0x3e, 0x3d, 0x1c, 0x85, 0xcd, 0xde, 0xa6, 0xf6, 0x4e, 0xd8, 0xed,
-	0xa8, 0xad, 0x55, 0xf2, 0x44, 0xd8, 0x7e, 0x85, 0x60, 0x79, 0xac, 0xe5, 0xcd, 0x80, 0xf8, 0x3e,
-	0x0d, 0xf0, 0x4d, 0x98, 0x79, 0xc4, 0x3f, 0x88, 0x68, 0xad, 0x36, 0xeb, 0x75, 0xbd, 0xa6, 0x8d,
-	0xd5, 0x72, 0xfb, 0x73, 0x56, 0x3c, 0x1d, 0xd7, 0x95, 0x0f, 0x4a, 0x42, 0xcf, 0x42, 0x46, 0x4f,
-	0xe2, 0x2a, 0x3e, 0x5e, 0x0c, 0xbb, 0x36, 0x0b, 0xd3, 0x3e, 0x09, 0x22, 0xf3, 0x28, 0xbc, 0x90,
-	0x0d, 0x43, 0x9f, 0x79, 0x21, 0x35, 0x7f, 0x87, 0x32, 0x1b, 0x7a, 0x3d, 0xa0, 0x24, 0xa2, 0x16,
-	0x7d, 0xd4, 0xa5, 0x61, 0x84, 0x77, 0x40, 0x2f, 0xb3, 0xc2, 0x77, 0xd5, 0xe6, 0x9d, 0x7a, 0x5a,
-	0xa7, 0xea, 0xaa, 0x4e, 0x89, 0xc6, 0xf7, 0x6c, 0xa7, 0xde, 0x6b, 0xd6, 0xfd, 0x9d, 0x56, 0x9d,
-	0x57, 0xbd, 0x0c, 0x32, 0x55, 0xf5, 0xf4, 0xa5, 0x5a, 0xba, 0x76, 0xbc, 0x00, 0xb3, 0x5d, 0x3f,
-	0xa4, 0x41, 0x24, 0x56, 0x56, 0xb6, 0xa4, 0xc4, 0x77, 0xa9, 0x47, 0xda, 0xae, 0x43, 0xa2, 0x78,
-	0x17, 0xca, 0x56, 0x22, 0x9b, 0x1f, 0x65, 0xd1, 0x3f, 0xf4, 0x9d, 0xcf, 0x0a, 0xbd, 0x8e, 0xb2,
-	0x34, 0x80, 0xf2, 0xfd, 0x2c, 0xca, 0xd7, 0x68, 0x9b, 0xa6, 0x28, 0xf3, 0x02, 0xd3, 0x80, 0x39,
-	0x9b, 0x84, 0x36, 0x71, 0x94, 0x2e, 0x25, 0xf2, 0xb2, 0xe0, 0x07, 0xcc, 0x27, 0x2d, 0xa1, 0xe9,
-	0x3e, 0x6b, 0xbb, 0x76, 0x5f, 0xc6, 0xe6, 0xf0, 0x87, 0xa1, 0x20, 0x9e, 0xce, 0x09, 0xe2, 0x93,
-	0x50, 0xdd, 0xe8, 0x7b, 0xf6, 0xeb, 0xbe, 0x38, 0x32, 0x79, 0x8a, 0xb9, 0x11, 0xed, 0x84, 0x06,
-	0x12, 0x75, 0x3f, 0x16, 0xcc, 0x0f, 0x66, 0x60, 0x41, 0x5b, 0x01, 0x9f, 0x50, 0x84, 0xbf, 0x28,
-	0xe9, 0x17, 0x60, 0xd6, 0x09, 0xfa, 0x56, 0xd7, 0x93, 0x9b, 0x29, 0x25, 0x6e, 0xd8, 0x0f, 0xba,
-	0x5e, 0x0c, 0xb2, 0x6c, 0xc5, 0x02, 0xde, 0x82, 0x72, 0x18, 0xf1, 0x43, 0xb2, 0xd5, 0x17, 0xe5,
-	0xa8, 0xda, 0xfc, 0xc6, 0xde, 0x36, 0x90, 0x43, 0xdf, 0x90, 0x1a, 0xad, 0x44, 0x37, 0x7e, 0x04,
-	0x15, 0x55, 0x09, 0x43, 0x63, 0x6e, 0x69, 0x6a, 0xb9, 0xda, 0xdc, 0xd8, 0xbb, 0xa1, 0xd7, 0x7d,
-	0x7e, 0xc0, 0x6b, 0x55, 0xdf, 0x4a, 0xad, 0xe0, 0x45, 0xa8, 0x74, 0x64, 0xae, 0x87, 0x46, 0x59,
-	0x78, 0x3b, 0xed, 0xc0, 0xdf, 0x81, 0x19, 0xd7, 0xdb, 0x62, 0xa1, 0x51, 0x11, 0x60, 0xae, 0xed,
-	0x0d, 0xcc, 0x1d, 0x6f, 0x8b, 0x59, 0xb1, 0x42, 0xfc, 0x08, 0x0e, 0x04, 0x34, 0x0a, 0xfa, 0xca,
-	0x0b, 0x06, 0x08, 0xbf, 0x7e, 0x73, 0x6f, 0x16, 0x2c, 0x5d, 0xa5, 0x95, 0xb5, 0x80, 0xd7, 0xa0,
-	0x1a, 0xa6, 0x31, 0x66, 0x54, 0x85, 0x41, 0x23, 0xa3, 0x48, 0x8b, 0x41, 0x4b, 0x1f, 0x3c, 0x14,
-	0xc3, 0xf3, 0x39, 0x31, 0xfc, 0x4f, 0x04, 0x8b, 0x43, 0x65, 0x60, 0xc3, 0xa7, 0x85, 0x41, 0x4a,
-	0x60, 0x3a, 0xf4, 0xa9, 0x2d, 0x2a, 0x7f, 0xb5, 0x79, 0x77, 0xdf, 0xea, 0x82, 0xb0, 0x2b, 0x54,
-	0x17, 0x95, 0xae, 0x89, 0x72, 0xf3, 0xc7, 0x08, 0x3e, 0xaf, 0x69, 0xbe, 0x4f, 0x22, 0x7b, 0xbb,
-	0x68, 0x49, 0x3c, 0x87, 0xf8, 0x18, 0x79, 0x9a, 0xc5, 0x02, 0x0f, 0x34, 0xd1, 0x78, 0xd0, 0xf7,
-	0x39, 0x0c, 0xfe, 0x25, 0xed, 0x98, 0xe8, 0xd0, 0x7f, 0x07, 0x41, 0x4d, 0xaf, 0x7c, 0xac, 0xdd,
-	0x7e, 0x93, 0xd8, 0x3b, 0x45, 0x50, 0x0e, 0x42, 0xc9, 0x75, 0x04, 0x8e, 0x29, 0xab, 0xe4, 0x3a,
-	0xbb, 0x4c, 0xfb, 0x41, 0x50, 0xb3, 0x39, 0xa0, 0x3e, 0x19, 0x00, 0xa5, 0x52, 0xac, 0x00, 0xd4,
-	0x22, 0x54, 0xbc, 0x81, 0xcb, 0x54, 0xda, 0x91, 0x73, 0x89, 0x2a, 0x0d, 0x5d, 0xa2, 0x0c, 0x98,
-	0xeb, 0x25, 0xb7, 0x5e, 0xfe, 0x59, 0x89, 0x7c, 0x21, 0xad, 0x80, 0x75, 0x7d, 0xe9, 0xc0, 0x58,
-	0xe0, 0x28, 0x76, 0x5c, 0xcf, 0x31, 0x66, 0x63, 0x14, 0xbc, 0x3d, 0xd1, 0x3d, 0xf7, 0xdd, 0x12,
-	0x7c, 0x21, 0x67, 0x71, 0x63, 0x23, 0xe0, 0xf9, 0x58, 0x61, 0x12, 0x87, 0x73, 0x23, 0xe3, 0xb0,
-	0x3c, 0x2e, 0x0e, 0x2b, 0x39, 0x5e, 0x79, 0xbb, 0x04, 0x4b, 0x39, 0x5e, 0x19, 0x7f, 0xa0, 0x3e,
-	0x37, 0x6e, 0xd9, 0x62, 0x81, 0xdc, 0xf1, 0xb2, 0x15, 0x0b, 0x3c, 0x33, 0x58, 0xe0, 0x6f, 0x13,
-	0xcf, 0x28, 0xc7, 0x99, 0x11, 0x4b, 0x13, 0x39, 0xe4, 0xbf, 0x08, 0x0c, 0xe5, 0x85, 0xab, 0xb6,
-	0xf0, 0x49, 0xd7, 0x7b, 0xfe, 0x1d, 0xb1, 0x00, 0xb3, 0x44, 0xa0, 0x95, 0x01, 0x22, 0xa5, 0xa1,
-	0x25, 0x97, 0xf3, 0x6b, 0xe2, 0xb1, 0xec, 0x92, 0xc3, 0x75, 0x37, 0x8c, 0xd4, 0x85, 0x16, 0x6f,
-	0xc1, 0x5c, 0xac, 0x2d, 0xbe, 0xc2, 0x54, 0x9b, 0xeb, 0x7b, 0x3d, 0xd8, 0x32, 0xee, 0x55, 0xca,
-	0xcd, 0x57, 0xe0, 0x58, 0x6e, 0xf5, 0x91, 0x30, 0x6a, 0x50, 0x56, 0x87, 0xb9, 0xdc, 0x80, 0x44,
-	0x36, 0xff, 0x33, 0x95, 0x2d, 0xeb, 0xcc, 0x59, 0x67, 0xad, 0x82, 0xb7, 0x60, 0xf1, 0xa6, 0x19,
-	0x30, 0xe7, 0x33, 0x47, 0x7b, 0xf6, 0x29, 0x91, 0xcf, 0xb3, 0x99, 0x17, 0x11, 0xd7, 0xa3, 0x81,
-	0x3c, 0x5f, 0xd2, 0x0e, 0xee, 0xec, 0xd0, 0xf5, 0x6c, 0xba, 0x41, 0x6d, 0xe6, 0x39, 0xa1, 0xd8,
-	0xb5, 0x29, 0x2b, 0xd3, 0x87, 0x6f, 0x43, 0x45, 0xc8, 0x0f, 0xdc, 0x4e, 0x5c, 0x84, 0xab, 0xcd,
-	0x95, 0x7a, 0x4c, 0x95, 0xd4, 0x75, 0xaa, 0x24, 0xf5, 0x61, 0x87, 0x46, 0xa4, 0xde, 0xbb, 0x54,
-	0xe7, 0x33, 0xac, 0x74, 0x32, 0xc7, 0x12, 0x11, 0xb7, 0xbd, 0xee, 0x7a, 0xe2, 0x82, 0xc5, 0x4d,
-	0xa5, 0x1d, 0x3c, 0x20, 0xb6, 0x58, 0xbb, 0xcd, 0x1e, 0xab, 0x1c, 0x88, 0x25, 0x3e, 0xab, 0xeb,
-	0x45, 0x6e, 0x5b, 0xd8, 0x8f, 0x13, 0x20, 0xed, 0x10, 0xb3, 0xdc, 0x76, 0x44, 0x03, 0x71, 0x85,
-	0xa9, 0x58, 0x52, 0x4a, 0x42, 0xae, 0x1a, 0xf3, 0x02, 0x2a, 0xf7, 0xe2, 0xe0, 0x9c, 0xd7, 0x83,
-	0x73, 0x30, 0xe0, 0x0f, 0xe4, 0xbc, 0x9b, 0x05, 0x19, 0x42, 0x7b, 0x2e, 0xeb, 0x86, 0xc6, 0xc1,
-	0xf8, 0x10, 0x57, 0xf2, 0x50, 0xc0, 0x1e, 0xca, 0x09, 0xd8, 0xdf, 0x23, 0x28, 0xaf, 0xb3, 0xd6,
-	0x0d, 0x2f, 0x0a, 0xfa, 0xe2, 0x66, 0xcf, 0xbc, 0x88, 0x7a, 0x2a, 0x2a, 0x94, 0xc8, 0x5d, 0x1d,
-	0xb9, 0x1d, 0xba, 0x11, 0x91, 0x8e, 0x2f, 0xef, 0x24, 0xbb, 0x72, 0x75, 0x32, 0x99, 0x2f, 0xbf,
-	0x4d, 0xc2, 0x48, 0x64, 0x6f, 0xd9, 0x12, 0x6d, 0x0e, 0x34, 0x19, 0xb0, 0x11, 0x05, 0x32, 0x75,
-	0x33, 0x7d, 0x7a, 0x20, 0xcd, 0xc4, 0xd8, 0xa4, 0x68, 0x6e, 0xc0, 0x4b, 0xc9, 0x55, 0xf6, 0x01,
-	0x0d, 0x3a, 0xae, 0x47, 0x8a, 0xeb, 0xed, 0x24, 0x2c, 0xcc, 0xc3, 0x4c, 0x02, 0xf1, 0xfb, 0xdf,
-	0xa6, 0xeb, 0x39, 0xec, 0x71, 0x41, 0x22, 0x4c, 0xa2, 0xf6, 0x6f, 0x59, 0xbe, 0x45, 0xd3, 0x9b,
-	0xe4, 0xe6, 0x6d, 0x38, 0xc0, 0xb3, 0xb8, 0x47, 0xe5, 0x07, 0x59, 0x28, 0xcc, 0x51, 0x4f, 0xf2,
-	0x54, 0x87, 0x95, 0x9d, 0x88, 0xd7, 0xe1, 0x10, 0x09, 0x43, 0xb7, 0xe5, 0x51, 0x47, 0xe9, 0x2a,
-	0x4d, 0xac, 0x6b, 0x70, 0x6a, 0xfc, 0xec, 0x13, 0x23, 0xe4, 0xde, 0x29, 0xd1, 0xfc, 0x11, 0x82,
-	0xa3, 0xb9, 0x4a, 0x92, 0x58, 0x47, 0x5a, 0x79, 0xad, 0x41, 0x39, 0xb4, 0xb7, 0xa9, 0xd3, 0x6d,
-	0x53, 0xc5, 0x6b, 0x28, 0x99, 0x7f, 0x73, 0xba, 0xf1, 0x4e, 0xca, 0xf2, 0x9e, 0xc8, 0xf8, 0x04,
-	0x40, 0x87, 0x78, 0x5d, 0xd2, 0x16, 0x10, 0xa6, 0x05, 0x04, 0xad, 0xc7, 0x5c, 0x84, 0x5a, 0x5e,
-	0x18, 0x48, 0x26, 0xe1, 0x1f, 0x08, 0x0e, 0xaa, 0x32, 0x28, 0xf7, 0x70, 0x19, 0x0e, 0x69, 0x6e,
-	0xb8, 0x97, 0x6e, 0xe7, 0x60, 0xf7, 0x98, 0x12, 0xa7, 0x62, 0x61, 0x2a, 0xcb, 0x5e, 0xf6, 0x32,
-	0xfc, 0xe3, 0xc4, 0xe7, 0x10, 0xda, 0xd5, 0x4d, 0xec, 0x87, 0x60, 0xdc, 0x25, 0x1e, 0x69, 0x51,
-	0x27, 0x59, 0x5c, 0x12, 0x48, 0xdf, 0xd7, 0x1f, 0xcb, 0x7b, 0x7e, 0x9a, 0x26, 0xd7, 0x19, 0x77,
-	0x6b, 0x4b, 0x3e, 0xbc, 0x9b, 0x3f, 0x59, 0x02, 0xac, 0x6f, 0x3c, 0x0d, 0x7a, 0xae, 0x4d, 0xf1,
-	0x3b, 0x08, 0xa6, 0xf9, 0xa9, 0x87, 0x8f, 0x8f, 0x8a, 0x33, 0xb1, 0x01, 0xb5, 0xfd, 0x7b, 0xd5,
-	0x70, 0x6b, 0xe6, 0xe2, 0x5b, 0x7f, 0xff, 0xf7, 0xbb, 0xa5, 0x05, 0xfc, 0xa2, 0xe0, 0xd2, 0x7b,
-	0x97, 0x74, 0x5e, 0x3b, 0xc4, 0x3f, 0x45, 0x80, 0xe5, 0x51, 0xac, 0x51, 0x9c, 0xf8, 0xfc, 0x28,
-	0x88, 0x39, 0x54, 0x68, 0xed, 0xb8, 0x56, 0xf2, 0xea, 0x36, 0x0b, 0x28, 0x2f, 0x70, 0x62, 0x80,
-	0x00, 0xb0, 0x22, 0x00, 0x9c, 0xc2, 0x66, 0x1e, 0x80, 0xc6, 0x13, 0x1e, 0x18, 0x4f, 0x1b, 0x34,
-	0xb6, 0xfb, 0x21, 0x82, 0x99, 0x4d, 0x71, 0xf1, 0x1c, 0xe3, 0xa4, 0x8d, 0x7d, 0x73, 0x92, 0x30,
-	0x27, 0xd0, 0x9a, 0x27, 0x05, 0xd2, 0xe3, 0xf8, 0x98, 0x42, 0x1a, 0x46, 0x01, 0x25, 0x9d, 0x0c,
-	0xe0, 0x8b, 0x08, 0x7f, 0x8c, 0x60, 0x36, 0xe6, 0xdc, 0xf0, 0xe9, 0x51, 0x28, 0x33, 0x9c, 0x5c,
-	0x6d, 0xff, 0x08, 0x2c, 0xf3, 0x9c, 0xc0, 0x78, 0xd2, 0xcc, 0xdd, 0xce, 0xb5, 0x0c, 0xbd, 0xf5,
-	0x1e, 0x82, 0xa9, 0x5b, 0x74, 0x6c, 0xbc, 0xed, 0x23, 0xb8, 0x21, 0x07, 0xe6, 0x6c, 0x35, 0xfe,
-	0x08, 0xc1, 0x4b, 0xb7, 0x68, 0x94, 0x5f, 0xef, 0xf1, 0xf2, 0xf8, 0x22, 0x2c, 0xc3, 0xee, 0xfc,
-	0x04, 0x23, 0x93, 0x42, 0xd7, 0x10, 0xc8, 0xce, 0xe1, 0xb3, 0x45, 0x41, 0x18, 0xf6, 0x3d, 0xfb,
-	0xb1, 0xc4, 0xf1, 0x67, 0x04, 0x87, 0x07, 0x7f, 0x70, 0xc0, 0xd9, 0x13, 0x22, 0xf7, 0xf7, 0x88,
-	0xda, 0xbd, 0xbd, 0x16, 0x94, 0xac, 0x52, 0xf3, 0xaa, 0x40, 0xfe, 0x2a, 0x7e, 0xa5, 0x08, 0xb9,
-	0x62, 0xea, 0xc2, 0xc6, 0x13, 0xd5, 0x7c, 0x2a, 0x7e, 0x01, 0x13, 0xb0, 0xdf, 0x42, 0x30, 0x7f,
-	0x8b, 0x46, 0x77, 0x13, 0xa2, 0xea, 0xf4, 0x44, 0x44, 0x76, 0x6d, 0xb1, 0xae, 0xfd, 0x50, 0xa5,
-	0x3e, 0x25, 0x2e, 0x5d, 0x15, 0xc0, 0xce, 0xe2, 0xd3, 0x45, 0xc0, 0x52, 0x72, 0xec, 0x43, 0x04,
-	0x47, 0x75, 0x10, 0x29, 0xcd, 0xff, 0xa5, 0xdd, 0xd1, 0xea, 0x92, 0x9c, 0x1f, 0x83, 0xae, 0x29,
-	0xd0, 0x5d, 0x30, 0xf3, 0x37, 0xbc, 0x33, 0x84, 0x62, 0x0d, 0xad, 0x2c, 0x23, 0xfc, 0x07, 0x04,
-	0xb3, 0x31, 0x13, 0x35, 0xda, 0x47, 0x19, 0xc2, 0x7a, 0x3f, 0xb3, 0xe7, 0x86, 0x80, 0xfc, 0xb5,
-	0xda, 0xc5, 0x7c, 0x87, 0xea, 0xf3, 0xd5, 0xd6, 0xd6, 0x85, 0x97, 0xb3, 0x69, 0xff, 0x1b, 0x04,
-	0x90, 0xb2, 0x69, 0xf8, 0x5c, 0xf1, 0x3a, 0x34, 0xc6, 0xad, 0xb6, 0xbf, 0x7c, 0x9a, 0x59, 0x17,
-	0xeb, 0x59, 0xae, 0x2d, 0x15, 0xe6, 0x9c, 0x4f, 0xed, 0xb5, 0x98, 0x79, 0xfb, 0x25, 0x82, 0x19,
-	0x41, 0x96, 0xe0, 0x53, 0xa3, 0x30, 0xeb, 0x5c, 0xca, 0x7e, 0xba, 0xfe, 0x8c, 0x80, 0xba, 0xd4,
-	0x2c, 0x2a, 0x5c, 0x6b, 0x68, 0x05, 0xf7, 0x60, 0x36, 0x26, 0x2e, 0x46, 0x87, 0x47, 0x86, 0xd8,
-	0xa8, 0x2d, 0x15, 0x1c, 0xa4, 0x71, 0xa0, 0xca, 0x9a, 0xb9, 0x32, 0xae, 0x66, 0x4e, 0xf3, 0xb2,
-	0x86, 0x4f, 0x16, 0x15, 0xbd, 0x4f, 0xc1, 0x31, 0xe7, 0x05, 0xba, 0xd3, 0xe6, 0xd2, 0xb8, 0xba,
-	0xc9, 0xbd, 0xf3, 0x73, 0x04, 0x87, 0x07, 0xef, 0x5d, 0xf8, 0xd8, 0x40, 0xcd, 0xd4, 0x2f, 0x9b,
-	0xb5, 0xac, 0x17, 0x47, 0xdd, 0xd9, 0xcc, 0xaf, 0x0b, 0x14, 0x6b, 0xf8, 0xca, 0xd8, 0xcc, 0xb8,
-	0xa7, 0xaa, 0x0e, 0x57, 0xb4, 0x9a, 0x12, 0xf7, 0xbf, 0x45, 0x30, 0xaf, 0xf4, 0x3e, 0x08, 0x28,
-	0x2d, 0x86, 0xb5, 0x7f, 0x89, 0xc0, 0x6d, 0x99, 0x5f, 0x11, 0xf0, 0xbf, 0x8c, 0x2f, 0x4f, 0x08,
-	0x5f, 0xc1, 0x5e, 0x8d, 0x38, 0xd2, 0x3f, 0x22, 0x38, 0xb2, 0x19, 0xc7, 0xfd, 0x67, 0x84, 0xff,
-	0xba, 0xc0, 0xff, 0x55, 0xfc, 0x6a, 0xc1, 0xbd, 0x68, 0xdc, 0x32, 0x2e, 0x22, 0xfc, 0x6b, 0x04,
-	0x65, 0x45, 0x43, 0xe3, 0xb3, 0x23, 0x13, 0x23, 0x4b, 0x54, 0xef, 0x67, 0x30, 0xcb, 0x4b, 0x80,
-	0x79, 0xaa, 0xf0, 0x28, 0x95, 0xf6, 0x79, 0x40, 0xbf, 0x87, 0x00, 0x27, 0x8f, 0xa6, 0xe4, 0x19,
-	0x85, 0xcf, 0x64, 0x4c, 0x8d, 0x7c, 0x65, 0xd7, 0xce, 0x8e, 0x1d, 0x97, 0x3d, 0x4a, 0x57, 0x0a,
-	0x8f, 0x52, 0x96, 0xd8, 0x7f, 0x1b, 0x41, 0xf5, 0x16, 0x4d, 0xee, 0xec, 0x05, 0xbe, 0xcc, 0xf2,
-	0xeb, 0xb5, 0xe5, 0xf1, 0x03, 0x25, 0xa2, 0x0b, 0x02, 0xd1, 0x19, 0x5c, 0xec, 0x2a, 0x05, 0xe0,
-	0x03, 0x04, 0x07, 0xee, 0xeb, 0x21, 0x8a, 0x2f, 0x8c, 0xb3, 0x94, 0xa9, 0xe4, 0x93, 0xe3, 0xfa,
-	0xa2, 0xc0, 0xb5, 0x6a, 0x4e, 0x84, 0x6b, 0x4d, 0x92, 0xd8, 0xbf, 0x40, 0xf0, 0x82, 0xfe, 0xc8,
-	0x91, 0x14, 0xe4, 0xff, 0xeb, 0xb7, 0x02, 0x26, 0xd3, 0xbc, 0x2c, 0xf0, 0xd5, 0xf1, 0x85, 0x49,
-	0xf0, 0x35, 0x24, 0x2f, 0x89, 0xdf, 0x47, 0x70, 0x44, 0x90, 0xc0, 0xba, 0xe2, 0x81, 0x23, 0x66,
-	0x14, 0x65, 0x3c, 0xc1, 0x11, 0x23, 0xeb, 0x8f, 0xb9, 0x2b, 0x50, 0x6b, 0x8a, 0xe0, 0xfd, 0x19,
-	0x82, 0x83, 0xea, 0x50, 0x93, 0xbb, 0xbb, 0x3a, 0xce, 0x71, 0xbb, 0x3d, 0x04, 0x65, 0xb8, 0xad,
-	0x4c, 0x16, 0x6e, 0x1f, 0x23, 0x98, 0x93, 0x04, 0x6c, 0xc1, 0x55, 0x41, 0x63, 0x68, 0x6b, 0x47,
-	0x33, 0xa3, 0x14, 0xb3, 0x67, 0x7e, 0x57, 0x98, 0x7d, 0x88, 0x1b, 0x45, 0x66, 0x7d, 0xe6, 0x84,
-	0x8d, 0x27, 0x92, 0x56, 0x7b, 0xda, 0x68, 0xb3, 0x56, 0xf8, 0x86, 0x89, 0x0b, 0x0f, 0x44, 0x3e,
-	0xe6, 0x22, 0xba, 0x76, 0xf3, 0x4f, 0xcf, 0x4e, 0xa0, 0xbf, 0x3e, 0x3b, 0x81, 0xfe, 0xf5, 0xec,
-	0x04, 0x7a, 0xe3, 0xca, 0x64, 0x7f, 0x3c, 0xb3, 0xdb, 0x2e, 0xf5, 0x22, 0x5d, 0xed, 0xff, 0x02,
-	0x00, 0x00, 0xff, 0xff, 0xa8, 0x3a, 0x7a, 0xdf, 0x5e, 0x27, 0x00, 0x00,
+	0x15, 0xa7, 0x66, 0xbf, 0x66, 0xde, 0xac, 0xbf, 0x2a, 0xf1, 0xd2, 0x69, 0xaf, 0xcd, 0xba, 0xfd,
+	0xb5, 0x5e, 0x7b, 0x67, 0xec, 0xc1, 0x20, 0x67, 0x13, 0x04, 0xb6, 0xe3, 0x2f, 0x58, 0x3b, 0xa6,
+	0xd7, 0xc6, 0x28, 0x1c, 0xa0, 0xd2, 0x53, 0x3b, 0xdb, 0x6c, 0x4f, 0x77, 0xbb, 0xbb, 0x67, 0xac,
+	0x91, 0xf1, 0x25, 0x88, 0x13, 0x51, 0x90, 0x92, 0x1c, 0x50, 0x14, 0x21, 0x94, 0x28, 0x17, 0x2e,
+	0xdc, 0x10, 0x12, 0x17, 0xb8, 0x20, 0x90, 0x38, 0x20, 0x3e, 0x2e, 0x39, 0x21, 0x8b, 0x1b, 0x17,
+	0xfe, 0x04, 0x54, 0xd5, 0x55, 0xdd, 0xd5, 0x33, 0x3d, 0x3d, 0xbd, 0xec, 0x46, 0xf1, 0xad, 0x5e,
+	0x4d, 0xd5, 0x7b, 0xbf, 0x7a, 0xf5, 0xbe, 0xea, 0xf5, 0xc0, 0xc9, 0x90, 0x06, 0x7d, 0x1a, 0x34,
+	0x89, 0xef, 0x3b, 0xb6, 0x45, 0x22, 0xdb, 0x73, 0xd5, 0x71, 0xc3, 0x0f, 0xbc, 0xc8, 0xc3, 0x75,
+	0x65, 0x4a, 0x5f, 0xec, 0x78, 0x5e, 0xc7, 0xa1, 0x4d, 0xe2, 0xdb, 0x4d, 0xe2, 0xba, 0x5e, 0xc4,
+	0xa7, 0xc3, 0x78, 0xa9, 0x6e, 0x6c, 0x5f, 0x0e, 0x1b, 0xb6, 0xc7, 0x7f, 0xb5, 0xbc, 0x80, 0x36,
+	0xfb, 0x17, 0x9b, 0x1d, 0xea, 0xd2, 0x80, 0x44, 0xb4, 0x2d, 0xd6, 0x5c, 0x4a, 0xd7, 0x74, 0x89,
+	0xb5, 0x65, 0xbb, 0x34, 0x18, 0x34, 0xfd, 0xed, 0x0e, 0x9b, 0x08, 0x9b, 0x5d, 0x1a, 0x91, 0xbc,
+	0x5d, 0xeb, 0x1d, 0x3b, 0xda, 0xea, 0xbd, 0xd9, 0xb0, 0xbc, 0x6e, 0x93, 0x04, 0x1d, 0xcf, 0x0f,
+	0xbc, 0x1f, 0xf2, 0xc1, 0xaa, 0xd5, 0x6e, 0xf6, 0x5b, 0x29, 0x03, 0xf5, 0x2c, 0xfd, 0x8b, 0xc4,
+	0xf1, 0xb7, 0xc8, 0x28, 0xb7, 0xeb, 0x13, 0xb8, 0x05, 0xd4, 0xf7, 0x84, 0x6e, 0xf8, 0xd0, 0x8e,
+	0xbc, 0x60, 0xa0, 0x0c, 0x63, 0x36, 0xc6, 0xa7, 0x08, 0x0e, 0x5e, 0x49, 0xe5, 0x7d, 0xbb, 0x47,
+	0x83, 0x01, 0xc6, 0x30, 0xed, 0x92, 0x2e, 0xd5, 0xd0, 0x12, 0x5a, 0xae, 0x99, 0x7c, 0x8c, 0x35,
+	0x98, 0x0b, 0xe8, 0x66, 0x40, 0xc3, 0x2d, 0xad, 0xc2, 0xa7, 0x25, 0x89, 0x75, 0xa8, 0x32, 0xe1,
+	0xd4, 0x8a, 0x42, 0x6d, 0x6a, 0x69, 0x6a, 0xb9, 0x66, 0x26, 0x34, 0x5e, 0x86, 0x03, 0x01, 0x0d,
+	0xbd, 0x5e, 0x60, 0xd1, 0xef, 0xd0, 0x20, 0xb4, 0x3d, 0x57, 0x9b, 0xe6, 0xbb, 0x87, 0xa7, 0x19,
+	0x97, 0x90, 0x3a, 0xd4, 0x8a, 0xbc, 0x40, 0x9b, 0xe1, 0x4b, 0x12, 0x9a, 0xe1, 0x61, 0xc0, 0xb5,
+	0xd9, 0x18, 0x0f, 0x1b, 0x63, 0x03, 0xe6, 0x89, 0xef, 0xdf, 0x25, 0x5d, 0x1a, 0xfa, 0xc4, 0xa2,
+	0xda, 0x1c, 0xff, 0x2d, 0x33, 0x67, 0x5c, 0x83, 0xda, 0x5d, 0xaf, 0x4d, 0xc7, 0x1f, 0x6a, 0x98,
+	0x49, 0x25, 0x87, 0xc9, 0x36, 0x1c, 0x36, 0x69, 0xdf, 0x66, 0x20, 0xef, 0xd0, 0x88, 0xb4, 0x49,
+	0x44, 0x86, 0x19, 0x56, 0x12, 0x86, 0x3a, 0x54, 0x03, 0xb1, 0x58, 0xab, 0xf0, 0xf9, 0x84, 0x1e,
+	0x11, 0x36, 0x95, 0x23, 0xec, 0x2f, 0x08, 0x8e, 0x29, 0xd7, 0x61, 0x0a, 0x25, 0x5d, 0xef, 0x53,
+	0x37, 0x0a, 0xc7, 0x8b, 0x3d, 0x0f, 0x87, 0xa4, 0x3e, 0x87, 0x0f, 0x33, 0xfa, 0x03, 0x03, 0xa2,
+	0x4e, 0x4a, 0x20, 0xea, 0x1c, 0x5e, 0x82, 0xba, 0xa4, 0x1f, 0xdc, 0x7e, 0x4d, 0x5c, 0x9a, 0x3a,
+	0x35, 0x72, 0x9c, 0x99, 0x9c, 0xe3, 0xb8, 0xa0, 0x29, 0xa7, 0xb9, 0x43, 0x5c, 0x7b, 0x93, 0x86,
+	0x51, 0x59, 0xf5, 0xa1, 0x1d, 0xab, 0xef, 0x38, 0xd4, 0x6e, 0xd8, 0x0e, 0xbd, 0xb6, 0xd5, 0x73,
+	0xb7, 0xf1, 0x8b, 0x30, 0x63, 0xb1, 0x01, 0x97, 0x30, 0x6f, 0xc6, 0x84, 0xf1, 0x18, 0x8e, 0x8f,
+	0x83, 0xf4, 0xd0, 0x8e, 0xb6, 0xd8, 0xf6, 0x70, 0x1c, 0x36, 0x6b, 0x8b, 0x5a, 0xdb, 0x61, 0xaf,
+	0x2b, 0xaf, 0x56, 0xd2, 0xa5, 0xb0, 0xfd, 0x0a, 0xc1, 0xf2, 0x44, 0xc9, 0x0f, 0x03, 0xe2, 0xfb,
+	0x34, 0xc0, 0x37, 0x60, 0xe6, 0x11, 0xfb, 0x81, 0x5b, 0x6b, 0xbd, 0xd5, 0x68, 0xa8, 0x31, 0x6d,
+	0x22, 0x97, 0x5b, 0x5f, 0x30, 0xe3, 0xed, 0xb8, 0x21, 0x75, 0x50, 0xe1, 0x7c, 0x16, 0x32, 0x7c,
+	0x12, 0x55, 0xb1, 0xf5, 0x7c, 0xd9, 0xd5, 0x59, 0x98, 0xf6, 0x49, 0x10, 0x19, 0x87, 0xe1, 0x85,
+	0xac, 0x19, 0xfa, 0x9e, 0x1b, 0x52, 0xe3, 0x77, 0x28, 0x73, 0xa1, 0xd7, 0x02, 0x4a, 0x22, 0x6a,
+	0xd2, 0x47, 0x3d, 0x1a, 0x46, 0x78, 0x1b, 0xd4, 0x30, 0xcb, 0x75, 0x57, 0x6f, 0xdd, 0x6e, 0xa4,
+	0x71, 0xaa, 0x21, 0xe3, 0x14, 0x1f, 0x7c, 0xdf, 0x6a, 0x37, 0xfa, 0xad, 0x86, 0xbf, 0xdd, 0x69,
+	0xb0, 0xa8, 0x97, 0x41, 0x26, 0xa3, 0x9e, 0x7a, 0x54, 0x53, 0xe5, 0x8e, 0x17, 0x60, 0xb6, 0xe7,
+	0x87, 0x34, 0x88, 0xf8, 0xc9, 0xaa, 0xa6, 0xa0, 0xd8, 0x2d, 0xf5, 0x89, 0x63, 0xb7, 0x49, 0x14,
+	0xdf, 0x42, 0xd5, 0x4c, 0x68, 0xe3, 0xe3, 0x2c, 0xfa, 0x07, 0x7e, 0xfb, 0xf3, 0x42, 0xaf, 0xa2,
+	0xac, 0x0c, 0xa1, 0xfc, 0x20, 0x8b, 0xf2, 0x35, 0xea, 0xd0, 0x14, 0x65, 0x9e, 0x61, 0x6a, 0x30,
+	0x67, 0x91, 0xd0, 0x22, 0x6d, 0xc9, 0x4b, 0x92, 0x2c, 0x2c, 0xf8, 0x81, 0xe7, 0x93, 0x0e, 0xe7,
+	0x74, 0xcf, 0x73, 0x6c, 0x6b, 0x20, 0x6c, 0x73, 0xf4, 0x87, 0x11, 0x23, 0x9e, 0xce, 0x31, 0xe2,
+	0x13, 0x50, 0xdf, 0x18, 0xb8, 0xd6, 0xeb, 0x3e, 0x4f, 0x99, 0xcc, 0xc5, 0xec, 0x88, 0x76, 0x43,
+	0x0d, 0xf1, 0xb8, 0x1f, 0x13, 0xc6, 0x87, 0x33, 0xb0, 0xa0, 0x9c, 0x80, 0x6d, 0x28, 0xc2, 0x5f,
+	0xe4, 0xf4, 0x0b, 0x30, 0xdb, 0x0e, 0x06, 0x66, 0xcf, 0x15, 0x97, 0x29, 0x28, 0x26, 0xd8, 0x0f,
+	0x7a, 0x6e, 0x0c, 0xb2, 0x6a, 0xc6, 0x04, 0xde, 0x84, 0x6a, 0x18, 0xb1, 0x24, 0xd9, 0x19, 0xf0,
+	0x70, 0x54, 0x6f, 0x7d, 0x73, 0x77, 0x17, 0xc8, 0xa0, 0x6f, 0x08, 0x8e, 0x66, 0xc2, 0x1b, 0x3f,
+	0x82, 0x9a, 0x8c, 0x84, 0xa1, 0x36, 0xb7, 0x34, 0xb5, 0x5c, 0x6f, 0x6d, 0xec, 0x5e, 0xd0, 0xeb,
+	0x3e, 0x4b, 0xf0, 0x4a, 0xd4, 0x37, 0x53, 0x29, 0x78, 0x11, 0x6a, 0x5d, 0xe1, 0xeb, 0xa1, 0x56,
+	0xe5, 0xda, 0x4e, 0x27, 0xf0, 0x77, 0x61, 0xc6, 0x76, 0x37, 0xbd, 0x50, 0xab, 0x71, 0x30, 0x57,
+	0x77, 0x07, 0xe6, 0xb6, 0xbb, 0xe9, 0x99, 0x31, 0x43, 0xfc, 0x08, 0xf6, 0x05, 0x34, 0x0a, 0x06,
+	0x52, 0x0b, 0x1a, 0x70, 0xbd, 0x7e, 0x6b, 0x77, 0x12, 0x4c, 0x95, 0xa5, 0x99, 0x95, 0x80, 0xd7,
+	0xa0, 0x1e, 0xa6, 0x36, 0xa6, 0xd5, 0xb9, 0x40, 0x2d, 0xc3, 0x48, 0xb1, 0x41, 0x53, 0x5d, 0x3c,
+	0x62, 0xc3, 0xf3, 0x39, 0x36, 0xfc, 0x4f, 0x04, 0x8b, 0x23, 0x61, 0x60, 0xc3, 0xa7, 0x85, 0x46,
+	0x4a, 0x60, 0x3a, 0xf4, 0xa9, 0xc5, 0x23, 0x7f, 0xbd, 0x75, 0x67, 0xcf, 0xe2, 0x02, 0x97, 0xcb,
+	0x59, 0x17, 0x85, 0xae, 0x52, 0xbe, 0xf9, 0x13, 0x04, 0x5f, 0x54, 0x38, 0xdf, 0x23, 0x91, 0xb5,
+	0x55, 0x74, 0x24, 0xe6, 0x43, 0x6c, 0x8d, 0xc8, 0x66, 0x31, 0xc1, 0x0c, 0x8d, 0x0f, 0xee, 0x0f,
+	0x7c, 0x06, 0x83, 0xfd, 0x92, 0x4e, 0x94, 0x4a, 0xfa, 0xef, 0x22, 0xd0, 0xd5, 0xc8, 0xe7, 0x39,
+	0xce, 0x9b, 0xc4, 0xda, 0x2e, 0x82, 0xb2, 0x1f, 0x2a, 0x76, 0x9b, 0xe3, 0x98, 0x32, 0x2b, 0x76,
+	0x7b, 0x87, 0x6e, 0x3f, 0x0c, 0x6a, 0x36, 0x07, 0xd4, 0xa7, 0x43, 0xa0, 0xa4, 0x8b, 0x15, 0x80,
+	0x5a, 0x84, 0x9a, 0x3b, 0x54, 0x4c, 0xa5, 0x13, 0x39, 0x45, 0x54, 0x65, 0xa4, 0x88, 0xd2, 0x60,
+	0xae, 0x9f, 0x54, 0xbd, 0xec, 0x67, 0x49, 0xb2, 0x83, 0x74, 0x02, 0xaf, 0xe7, 0x0b, 0x05, 0xc6,
+	0x04, 0x43, 0xb1, 0x6d, 0xbb, 0x6d, 0x6d, 0x36, 0x46, 0xc1, 0xc6, 0xa5, 0xea, 0xdc, 0xf7, 0x2a,
+	0xf0, 0xa5, 0x9c, 0xc3, 0x4d, 0xb4, 0x80, 0xe7, 0xe3, 0x84, 0x89, 0x1d, 0xce, 0x8d, 0xb5, 0xc3,
+	0xea, 0x24, 0x3b, 0xac, 0xe5, 0x68, 0xe5, 0x9d, 0x0a, 0x2c, 0xe5, 0x68, 0x65, 0x72, 0x42, 0x7d,
+	0x6e, 0xd4, 0xb2, 0xe9, 0x05, 0xe2, 0xc6, 0xab, 0x66, 0x4c, 0x30, 0xcf, 0xf0, 0x02, 0x7f, 0x8b,
+	0xb8, 0x5a, 0x35, 0xf6, 0x8c, 0x98, 0x2a, 0xa5, 0x90, 0xff, 0x22, 0xd0, 0xa4, 0x16, 0xae, 0x58,
+	0x5c, 0x27, 0x3d, 0xf7, 0xf9, 0x57, 0xc4, 0x02, 0xcc, 0x12, 0x8e, 0x56, 0x18, 0x88, 0xa0, 0x46,
+	0x8e, 0x5c, 0xcd, 0x8f, 0x89, 0x47, 0xb2, 0x47, 0x0e, 0xd7, 0xed, 0x30, 0x92, 0x05, 0x2d, 0xde,
+	0x84, 0xb9, 0x98, 0x5b, 0x5c, 0xc2, 0xd4, 0x5b, 0xeb, 0xbb, 0x4d, 0x6c, 0x19, 0xf5, 0x4a, 0xe6,
+	0xc6, 0xcb, 0x70, 0x24, 0x37, 0xfa, 0x08, 0x18, 0x3a, 0x54, 0x65, 0x32, 0x17, 0x17, 0x90, 0xd0,
+	0xc6, 0x7f, 0xa6, 0xb2, 0x61, 0xdd, 0x6b, 0xaf, 0x7b, 0x9d, 0x82, 0xb7, 0x60, 0xf1, 0xa5, 0x69,
+	0x30, 0xe7, 0x7b, 0x6d, 0xe5, 0xd9, 0x27, 0x49, 0xb6, 0xcf, 0xf2, 0xdc, 0x88, 0xd8, 0x2e, 0x0d,
+	0x44, 0x7e, 0x49, 0x27, 0x98, 0xb2, 0x43, 0xdb, 0xb5, 0xe8, 0x06, 0xb5, 0x3c, 0xb7, 0x1d, 0xf2,
+	0x5b, 0x9b, 0x32, 0x33, 0x73, 0xf8, 0x16, 0xd4, 0x38, 0x7d, 0xdf, 0xee, 0xc6, 0x41, 0xb8, 0xde,
+	0x5a, 0x69, 0xc4, 0xad, 0x92, 0x86, 0xda, 0x2a, 0x49, 0x75, 0xd8, 0xa5, 0x11, 0x69, 0xf4, 0x2f,
+	0x36, 0xd8, 0x0e, 0x33, 0xdd, 0xcc, 0xb0, 0x44, 0xc4, 0x76, 0xd6, 0x6d, 0x97, 0x17, 0x58, 0x4c,
+	0x54, 0x3a, 0xc1, 0x0c, 0x62, 0xd3, 0x73, 0x1c, 0xef, 0xb1, 0xf4, 0x81, 0x98, 0x62, 0xbb, 0x7a,
+	0x6e, 0x64, 0x3b, 0x5c, 0x7e, 0xec, 0x00, 0xe9, 0x04, 0xdf, 0x65, 0x3b, 0x11, 0x0d, 0x78, 0x09,
+	0x53, 0x33, 0x05, 0x95, 0x98, 0x5c, 0x3d, 0xee, 0x0b, 0x48, 0xdf, 0x8b, 0x8d, 0x73, 0x5e, 0x35,
+	0xce, 0x61, 0x83, 0xdf, 0x97, 0xf3, 0x6e, 0xe6, 0xcd, 0x10, 0xda, 0xb7, 0xbd, 0x5e, 0xa8, 0xed,
+	0x8f, 0x93, 0xb8, 0xa4, 0x47, 0x0c, 0xf6, 0x40, 0x8e, 0xc1, 0xfe, 0x1e, 0x41, 0x75, 0xdd, 0xeb,
+	0x5c, 0x77, 0xa3, 0x60, 0xc0, 0x2b, 0x7b, 0xcf, 0x8d, 0xa8, 0x2b, 0xad, 0x42, 0x92, 0x4c, 0xd5,
+	0x91, 0xdd, 0xa5, 0x1b, 0x11, 0xe9, 0xfa, 0xa2, 0x26, 0xd9, 0x91, 0xaa, 0x93, 0xcd, 0xec, 0xf8,
+	0x0e, 0x09, 0x23, 0xee, 0xbd, 0x55, 0x93, 0x8f, 0x19, 0xd0, 0x64, 0xc1, 0x46, 0x14, 0x08, 0xd7,
+	0xcd, 0xcc, 0xa9, 0x86, 0x34, 0x13, 0x63, 0x13, 0xa4, 0xb1, 0x01, 0x2f, 0x25, 0xa5, 0xec, 0x7d,
+	0x1a, 0x74, 0x6d, 0x97, 0x14, 0xc7, 0xdb, 0x32, 0x5d, 0x98, 0x07, 0x19, 0x07, 0x62, 0xf5, 0xdf,
+	0x43, 0xdb, 0x6d, 0x7b, 0x8f, 0x0b, 0x1c, 0xa1, 0x0c, 0xdb, 0xbf, 0x65, 0xfb, 0x2d, 0x0a, 0xdf,
+	0xc4, 0x37, 0x6f, 0xc1, 0x3e, 0xe6, 0xc5, 0x7d, 0x2a, 0x7e, 0x10, 0x81, 0xc2, 0x18, 0xf7, 0x24,
+	0x4f, 0x79, 0x98, 0xd9, 0x8d, 0x78, 0x1d, 0x0e, 0x90, 0x30, 0xb4, 0x3b, 0x2e, 0x6d, 0x4b, 0x5e,
+	0x95, 0xd2, 0xbc, 0x86, 0xb7, 0xc6, 0xcf, 0x3e, 0xbe, 0x42, 0xdc, 0x9d, 0x24, 0x8d, 0x1f, 0x23,
+	0x38, 0x9c, 0xcb, 0x24, 0xb1, 0x75, 0xa4, 0x84, 0x57, 0x1d, 0xaa, 0xa1, 0xb5, 0x45, 0xdb, 0x3d,
+	0x87, 0xca, 0xbe, 0x86, 0xa4, 0xd9, 0x6f, 0xed, 0x5e, 0x7c, 0x93, 0x22, 0xbc, 0x27, 0x34, 0x3e,
+	0x06, 0xd0, 0x25, 0x6e, 0x8f, 0x38, 0x1c, 0xc2, 0x34, 0x87, 0xa0, 0xcc, 0x18, 0x8b, 0xa0, 0xe7,
+	0x99, 0x81, 0xe8, 0x24, 0xfc, 0x03, 0xc1, 0x7e, 0x19, 0x06, 0xc5, 0x1d, 0x2e, 0xc3, 0x01, 0x45,
+	0x0d, 0x77, 0xd3, 0xeb, 0x1c, 0x9e, 0x9e, 0x10, 0xe2, 0xa4, 0x2d, 0x4c, 0x65, 0xbb, 0x97, 0xfd,
+	0x4c, 0xff, 0xb1, 0x74, 0x1e, 0x42, 0x3b, 0xaa, 0xc4, 0x7e, 0x04, 0xda, 0x1d, 0xe2, 0x92, 0x0e,
+	0x6d, 0x27, 0x87, 0x4b, 0x0c, 0xe9, 0x07, 0xea, 0x63, 0x79, 0xd7, 0x4f, 0xd3, 0xa4, 0x9c, 0xb1,
+	0x37, 0x37, 0xe5, 0xc3, 0x3b, 0x80, 0xea, 0xba, 0xed, 0x6e, 0xb3, 0xf7, 0x1b, 0x3b, 0x57, 0x64,
+	0x47, 0x8e, 0xd4, 0x61, 0x4c, 0xe0, 0x83, 0x30, 0xd5, 0x0b, 0x1c, 0x71, 0xcf, 0x6c, 0x88, 0x97,
+	0xa0, 0xde, 0xa6, 0xa1, 0x15, 0xd8, 0xbe, 0xb8, 0x65, 0xde, 0xe8, 0x53, 0xa6, 0x98, 0xb6, 0x6d,
+	0xcb, 0x73, 0xaf, 0x39, 0x24, 0x0c, 0x65, 0x62, 0x48, 0x26, 0x8c, 0x57, 0x61, 0x1f, 0x93, 0x99,
+	0x1e, 0xf3, 0x5c, 0xf6, 0x98, 0x87, 0x33, 0xf0, 0x25, 0x3c, 0x89, 0xf8, 0x26, 0xbc, 0xc0, 0xf2,
+	0xf1, 0x15, 0xdf, 0x17, 0x4c, 0x4a, 0x16, 0x23, 0x53, 0x43, 0x97, 0xde, 0xfa, 0xa9, 0x01, 0x58,
+	0xb5, 0x79, 0x1a, 0xf4, 0x6d, 0x8b, 0xe2, 0x77, 0x11, 0x4c, 0x33, 0x01, 0xf8, 0xe8, 0x38, 0x17,
+	0xe3, 0xb6, 0xa7, 0xef, 0xdd, 0x83, 0x8e, 0x49, 0x33, 0x16, 0xdf, 0xfa, 0xfb, 0xbf, 0xdf, 0xab,
+	0x2c, 0xe0, 0x17, 0xf9, 0x67, 0x84, 0xfe, 0x45, 0xb5, 0xa5, 0x1f, 0xe2, 0xb7, 0x11, 0x60, 0x51,
+	0x85, 0x28, 0xdd, 0x5d, 0x7c, 0x6e, 0x1c, 0xc4, 0x9c, 0x2e, 0xb0, 0x7e, 0x54, 0x89, 0xf6, 0x0d,
+	0xcb, 0x0b, 0x28, 0x8b, 0xed, 0x7c, 0x01, 0x07, 0xb0, 0xc2, 0x01, 0x9c, 0xc4, 0x46, 0x1e, 0x80,
+	0xe6, 0x13, 0xa6, 0xb7, 0xa7, 0x4d, 0x1a, 0xcb, 0xfd, 0x08, 0xc1, 0xcc, 0x43, 0x5e, 0x73, 0x4f,
+	0x50, 0xd2, 0xc6, 0x9e, 0x29, 0x89, 0x8b, 0xe3, 0x68, 0x8d, 0x13, 0x1c, 0xe9, 0x51, 0x7c, 0x44,
+	0x22, 0x0d, 0xa3, 0x80, 0x92, 0x6e, 0x06, 0xf0, 0x05, 0x84, 0x3f, 0x41, 0x30, 0x1b, 0xb7, 0x1b,
+	0xf1, 0xa9, 0x71, 0x28, 0x33, 0xed, 0x48, 0x7d, 0xef, 0x7a, 0x77, 0xc6, 0x59, 0x8e, 0xf1, 0x84,
+	0x91, 0x7b, 0x9d, 0x6b, 0x99, 0xce, 0xde, 0xfb, 0x08, 0xa6, 0x6e, 0xd2, 0x89, 0xf6, 0xb6, 0x87,
+	0xe0, 0x46, 0x14, 0x98, 0x73, 0xd5, 0xf8, 0x63, 0x04, 0x2f, 0xdd, 0xa4, 0x51, 0x7e, 0xaa, 0xc3,
+	0xcb, 0x93, 0xf3, 0x8f, 0x30, 0xbb, 0x73, 0x25, 0x56, 0x26, 0x31, 0xbe, 0xc9, 0x91, 0x9d, 0xc5,
+	0x67, 0x8a, 0x8c, 0x30, 0x1c, 0xb8, 0xd6, 0x63, 0x81, 0xe3, 0xcf, 0x08, 0x0e, 0x0e, 0x7f, 0x6b,
+	0xc1, 0xd9, 0xe4, 0x98, 0xfb, 0x29, 0x46, 0xbf, 0xbb, 0xdb, 0x58, 0x9a, 0x65, 0x6a, 0x5c, 0xe1,
+	0xc8, 0x5f, 0xc1, 0x2f, 0x17, 0x21, 0x97, 0x4d, 0xca, 0xb0, 0xf9, 0x44, 0x0e, 0x9f, 0xf2, 0x8f,
+	0x7f, 0x1c, 0xf6, 0x5b, 0x08, 0xe6, 0x6f, 0xd2, 0xe8, 0x4e, 0xd2, 0xa3, 0x3b, 0x55, 0xaa, 0x87,
+	0xaf, 0x2f, 0x36, 0x94, 0x6f, 0x74, 0xf2, 0xa7, 0x44, 0xa5, 0xab, 0x1c, 0xd8, 0x19, 0x7c, 0xaa,
+	0x08, 0x58, 0xda, 0x17, 0xfc, 0x08, 0xc1, 0x61, 0x15, 0x44, 0xfa, 0x85, 0xe3, 0x2b, 0x3b, 0xfb,
+	0xa2, 0x20, 0xbe, 0x4b, 0x4c, 0x40, 0xd7, 0xe2, 0xe8, 0xce, 0x1b, 0xf9, 0x17, 0xde, 0x1d, 0x41,
+	0xb1, 0x86, 0x56, 0x96, 0x11, 0xfe, 0x03, 0x82, 0xd9, 0xb8, 0x09, 0x37, 0x5e, 0x47, 0x99, 0x5e,
+	0xfd, 0x5e, 0x7a, 0xcf, 0x75, 0x0e, 0xf9, 0xeb, 0xfa, 0x85, 0x7c, 0x85, 0xaa, 0xfb, 0xe5, 0xd5,
+	0x36, 0xb8, 0x96, 0xb3, 0x6e, 0xff, 0x1b, 0x04, 0x90, 0x36, 0x12, 0xf1, 0xd9, 0xe2, 0x73, 0x28,
+	0xcd, 0x46, 0x7d, 0x6f, 0x5b, 0x89, 0x46, 0x83, 0x9f, 0x67, 0x59, 0x5f, 0x2a, 0xf4, 0x39, 0x9f,
+	0x5a, 0x6b, 0x71, 0xd3, 0xf1, 0x97, 0x08, 0x66, 0x78, 0x9f, 0x08, 0x9f, 0x1c, 0x87, 0x59, 0x6d,
+	0x23, 0xed, 0xa5, 0xea, 0x4f, 0x73, 0xa8, 0x4b, 0xad, 0xa2, 0xc0, 0xb5, 0x86, 0x56, 0x70, 0x1f,
+	0x66, 0xe3, 0x9e, 0xcd, 0x78, 0xf3, 0xc8, 0xf4, 0x74, 0xf4, 0xa5, 0x82, 0x44, 0x1a, 0x1b, 0xaa,
+	0x88, 0x99, 0x2b, 0x93, 0x62, 0xe6, 0x34, 0x0b, 0x6b, 0xf8, 0x44, 0x51, 0xd0, 0xfb, 0x0c, 0x14,
+	0x73, 0x8e, 0xa3, 0x3b, 0x65, 0x2c, 0x4d, 0x8a, 0x9b, 0x4c, 0x3b, 0x3f, 0x47, 0x70, 0x70, 0xb8,
+	0xe4, 0xc4, 0x47, 0x86, 0x62, 0xa6, 0x5a, 0x67, 0xeb, 0x59, 0x2d, 0x8e, 0x2b, 0x57, 0x8d, 0x6f,
+	0x70, 0x14, 0x6b, 0xf8, 0xf2, 0x44, 0xcf, 0xb8, 0x2b, 0xa3, 0x0e, 0x63, 0xb4, 0x9a, 0x7e, 0xb3,
+	0xf8, 0x2d, 0x82, 0x79, 0xc9, 0xf7, 0x7e, 0x40, 0x69, 0x31, 0xac, 0xbd, 0x73, 0x04, 0x26, 0xcb,
+	0x78, 0x95, 0xc3, 0xff, 0x2a, 0xbe, 0x54, 0x12, 0xbe, 0x84, 0xbd, 0x1a, 0x31, 0xa4, 0x7f, 0x44,
+	0x70, 0xe8, 0x61, 0x6c, 0xf7, 0x9f, 0x13, 0xfe, 0x6b, 0x1c, 0xff, 0xd7, 0xf0, 0x2b, 0x05, 0x75,
+	0xd1, 0xa4, 0x63, 0x5c, 0x40, 0xf8, 0xd7, 0x08, 0xaa, 0xb2, 0x03, 0x8f, 0xcf, 0x8c, 0x75, 0x8c,
+	0x6c, 0x8f, 0x7e, 0x2f, 0x8d, 0x59, 0x14, 0x01, 0xc6, 0xc9, 0xc2, 0x54, 0x2a, 0xe4, 0x33, 0x83,
+	0x7e, 0x1f, 0x01, 0x4e, 0xde, 0x8b, 0xc9, 0x0b, 0x12, 0x9f, 0xce, 0x88, 0x1a, 0xdb, 0x60, 0xd0,
+	0xcf, 0x4c, 0x5c, 0x97, 0x4d, 0xa5, 0x2b, 0x85, 0xa9, 0xd4, 0x4b, 0xe4, 0xbf, 0x83, 0xa0, 0x7e,
+	0x93, 0x26, 0x35, 0x7b, 0x81, 0x2e, 0xb3, 0x9f, 0x16, 0xf4, 0xe5, 0xc9, 0x0b, 0x05, 0xa2, 0xf3,
+	0x1c, 0xd1, 0x69, 0x5c, 0xac, 0x2a, 0x09, 0xe0, 0x43, 0x04, 0xfb, 0xee, 0xa9, 0x26, 0x8a, 0xcf,
+	0x4f, 0x92, 0x94, 0x89, 0xe4, 0xe5, 0x71, 0x7d, 0x99, 0xe3, 0x5a, 0x35, 0x4a, 0xe1, 0x5a, 0x13,
+	0xfd, 0xfb, 0x5f, 0xa0, 0xf8, 0x69, 0x37, 0xd4, 0x7d, 0xfd, 0x7f, 0xf5, 0x56, 0xd0, 0xc4, 0x35,
+	0x2e, 0x71, 0x7c, 0x0d, 0x7c, 0xbe, 0x0c, 0xbe, 0xa6, 0x68, 0xc9, 0xe2, 0x0f, 0x10, 0x1c, 0xe2,
+	0xfd, 0x6f, 0x95, 0xf1, 0x50, 0x8a, 0x19, 0xd7, 0x2d, 0x2f, 0x91, 0x62, 0x44, 0xfc, 0x31, 0x76,
+	0x04, 0x6a, 0x4d, 0xf6, 0xb6, 0x7f, 0x86, 0x60, 0xbf, 0x4c, 0x6a, 0xe2, 0x76, 0x57, 0x27, 0x29,
+	0x6e, 0xa7, 0x49, 0x50, 0x98, 0xdb, 0x4a, 0x39, 0x73, 0xfb, 0x04, 0xc1, 0x9c, 0xe8, 0x3d, 0x17,
+	0x94, 0x0a, 0x4a, 0x73, 0x5a, 0x1f, 0x7a, 0xf9, 0x8b, 0xa6, 0xa6, 0xf1, 0x3d, 0x2e, 0xf6, 0x01,
+	0x6e, 0x16, 0x89, 0xf5, 0xbd, 0x76, 0xd8, 0x7c, 0x22, 0x3a, 0x8a, 0x4f, 0x9b, 0x8e, 0xd7, 0x09,
+	0xdf, 0x30, 0x70, 0x61, 0x42, 0x64, 0x6b, 0x2e, 0x20, 0x1c, 0x41, 0x8d, 0x19, 0x07, 0x6f, 0x27,
+	0xe0, 0xa5, 0xa1, 0xe6, 0xc3, 0x48, 0xa7, 0x41, 0xd7, 0x47, 0xda, 0x13, 0x69, 0x06, 0x14, 0xcf,
+	0x3e, 0x7c, 0xbc, 0x50, 0x2c, 0x17, 0xf4, 0x36, 0x82, 0x43, 0xaa, 0xb5, 0xc7, 0xe2, 0x4b, 0xdb,
+	0x7a, 0x11, 0x0a, 0x51, 0x54, 0xe3, 0x95, 0x52, 0x86, 0xc4, 0xe1, 0x5c, 0xbd, 0xf1, 0xa7, 0x67,
+	0xc7, 0xd0, 0x5f, 0x9f, 0x1d, 0x43, 0xff, 0x7a, 0x76, 0x0c, 0xbd, 0x71, 0xb9, 0xdc, 0x1f, 0x0f,
+	0x2d, 0xc7, 0xa6, 0x6e, 0xa4, 0xb2, 0xff, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x83, 0x0c, 0x3f,
+	0x61, 0x5e, 0x29, 0x00, 0x00,
 }
 
 // Reference imports to suppress errors if they are not otherwise used.
@@ -2669,6 +2854,10 @@ type ApplicationServiceClient interface {
 	DeleteResource(ctx context.Context, in *ApplicationResourceDeleteRequest, opts ...grpc.CallOption) (*ApplicationResponse, error)
 	// PodLogs returns stream of log entries for the specified pod. Pod
 	PodLogs(ctx context.Context, in *ApplicationPodLogsQuery, opts ...grpc.CallOption) (ApplicationService_PodLogsClient, error)
+	// ListLinks returns the list of all application deep links
+	ListLinks(ctx context.Context, in *ListAppLinksRequest, opts ...grpc.CallOption) (*LinksResponse, error)
+	// ListResourceLinks returns the list of all resource deep links
+	ListResourceLinks(ctx context.Context, in *ApplicationResourceRequest, opts ...grpc.CallOption) (*LinksResponse, error)
 }
 
 type applicationServiceClient struct {
@@ -2998,6 +3187,24 @@ func (x *applicationServicePodLogsClient) Recv() (*LogEntry, error) {
 	return m, nil
 }
 
+func (c *applicationServiceClient) ListLinks(ctx context.Context, in *ListAppLinksRequest, opts ...grpc.CallOption) (*LinksResponse, error) {
+	out := new(LinksResponse)
+	err := c.cc.Invoke(ctx, "/application.ApplicationService/ListLinks", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *applicationServiceClient) ListResourceLinks(ctx context.Context, in *ApplicationResourceRequest, opts ...grpc.CallOption) (*LinksResponse, error) {
+	out := new(LinksResponse)
+	err := c.cc.Invoke(ctx, "/application.ApplicationService/ListResourceLinks", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
 // ApplicationServiceServer is the server API for ApplicationService service.
 type ApplicationServiceServer interface {
 	// List returns list of applications
@@ -3050,6 +3257,10 @@ type ApplicationServiceServer interface {
 	DeleteResource(context.Context, *ApplicationResourceDeleteRequest) (*ApplicationResponse, error)
 	// PodLogs returns stream of log entries for the specified pod. Pod
 	PodLogs(*ApplicationPodLogsQuery, ApplicationService_PodLogsServer) error
+	// ListLinks returns the list of all application deep links
+	ListLinks(context.Context, *ListAppLinksRequest) (*LinksResponse, error)
+	// ListResourceLinks returns the list of all resource deep links
+	ListResourceLinks(context.Context, *ApplicationResourceRequest) (*LinksResponse, error)
 }
 
 // UnimplementedApplicationServiceServer can be embedded to have forward compatible implementations.
@@ -3131,6 +3342,12 @@ func (*UnimplementedApplicationServiceServer) DeleteResource(ctx context.Context
 func (*UnimplementedApplicationServiceServer) PodLogs(req *ApplicationPodLogsQuery, srv ApplicationService_PodLogsServer) error {
 	return status.Errorf(codes.Unimplemented, "method PodLogs not implemented")
 }
+func (*UnimplementedApplicationServiceServer) ListLinks(ctx context.Context, req *ListAppLinksRequest) (*LinksResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method ListLinks not implemented")
+}
+func (*UnimplementedApplicationServiceServer) ListResourceLinks(ctx context.Context, req *ApplicationResourceRequest) (*LinksResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method ListResourceLinks not implemented")
+}
 
 func RegisterApplicationServiceServer(s *grpc.Server, srv ApplicationServiceServer) {
 	s.RegisterService(&_ApplicationService_serviceDesc, srv)
@@ -3603,6 +3820,42 @@ func (x *applicationServicePodLogsServer) Send(m *LogEntry) error {
 	return x.ServerStream.SendMsg(m)
 }
 
+func _ApplicationService_ListLinks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(ListAppLinksRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(ApplicationServiceServer).ListLinks(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/application.ApplicationService/ListLinks",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(ApplicationServiceServer).ListLinks(ctx, req.(*ListAppLinksRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _ApplicationService_ListResourceLinks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(ApplicationResourceRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(ApplicationServiceServer).ListResourceLinks(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/application.ApplicationService/ListResourceLinks",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(ApplicationServiceServer).ListResourceLinks(ctx, req.(*ApplicationResourceRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 var _ApplicationService_serviceDesc = grpc.ServiceDesc{
 	ServiceName: "application.ApplicationService",
 	HandlerType: (*ApplicationServiceServer)(nil),
@@ -3691,6 +3944,14 @@ var _ApplicationService_serviceDesc = grpc.ServiceDesc{
 			MethodName: "DeleteResource",
 			Handler:    _ApplicationService_DeleteResource_Handler,
 		},
+		{
+			MethodName: "ListLinks",
+			Handler:    _ApplicationService_ListLinks_Handler,
+		},
+		{
+			MethodName: "ListResourceLinks",
+			Handler:    _ApplicationService_ListResourceLinks_Handler,
+		},
 	},
 	Streams: []grpc.StreamDesc{
 		{
@@ -5790,90 +6051,233 @@ func (m *ManagedResourcesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error
 	return len(dAtA) - i, nil
 }
 
-func encodeVarintApplication(dAtA []byte, offset int, v uint64) int {
-	offset -= sovApplication(v)
-	base := offset
-	for v >= 1<<7 {
-		dAtA[offset] = uint8(v&0x7f | 0x80)
-		v >>= 7
-		offset++
+func (m *LinkInfo) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
 	}
-	dAtA[offset] = uint8(v)
-	return base
+	return dAtA[:n], nil
 }
-func (m *ApplicationQuery) Size() (n int) {
-	if m == nil {
-		return 0
-	}
+
+func (m *LinkInfo) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *LinkInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
 	var l int
 	_ = l
-	if m.Name != nil {
-		l = len(*m.Name)
-		n += 1 + l + sovApplication(uint64(l))
-	}
-	if m.Refresh != nil {
-		l = len(*m.Refresh)
-		n += 1 + l + sovApplication(uint64(l))
-	}
-	if len(m.Projects) > 0 {
-		for _, s := range m.Projects {
-			l = len(s)
-			n += 1 + l + sovApplication(uint64(l))
-		}
-	}
-	if m.ResourceVersion != nil {
-		l = len(*m.ResourceVersion)
-		n += 1 + l + sovApplication(uint64(l))
+	if m.XXX_unrecognized != nil {
+		i -= len(m.XXX_unrecognized)
+		copy(dAtA[i:], m.XXX_unrecognized)
 	}
-	if m.Selector != nil {
-		l = len(*m.Selector)
-		n += 1 + l + sovApplication(uint64(l))
+	if m.IconClass != nil {
+		i -= len(*m.IconClass)
+		copy(dAtA[i:], *m.IconClass)
+		i = encodeVarintApplication(dAtA, i, uint64(len(*m.IconClass)))
+		i--
+		dAtA[i] = 0x22
 	}
-	if m.Repo != nil {
-		l = len(*m.Repo)
-		n += 1 + l + sovApplication(uint64(l))
+	if m.Description != nil {
+		i -= len(*m.Description)
+		copy(dAtA[i:], *m.Description)
+		i = encodeVarintApplication(dAtA, i, uint64(len(*m.Description)))
+		i--
+		dAtA[i] = 0x1a
 	}
-	if m.AppNamespace != nil {
-		l = len(*m.AppNamespace)
-		n += 1 + l + sovApplication(uint64(l))
+	if m.Url == nil {
+		return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("url")
+	} else {
+		i -= len(*m.Url)
+		copy(dAtA[i:], *m.Url)
+		i = encodeVarintApplication(dAtA, i, uint64(len(*m.Url)))
+		i--
+		dAtA[i] = 0x12
 	}
-	if m.XXX_unrecognized != nil {
-		n += len(m.XXX_unrecognized)
+	if m.Title == nil {
+		return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("title")
+	} else {
+		i -= len(*m.Title)
+		copy(dAtA[i:], *m.Title)
+		i = encodeVarintApplication(dAtA, i, uint64(len(*m.Title)))
+		i--
+		dAtA[i] = 0xa
 	}
-	return n
+	return len(dAtA) - i, nil
 }
 
-func (m *NodeQuery) Size() (n int) {
-	if m == nil {
-		return 0
-	}
-	var l int
-	_ = l
-	if m.Name != nil {
-		l = len(*m.Name)
-		n += 1 + l + sovApplication(uint64(l))
-	}
-	if m.AppNamespace != nil {
-		l = len(*m.AppNamespace)
-		n += 1 + l + sovApplication(uint64(l))
-	}
-	if m.XXX_unrecognized != nil {
-		n += len(m.XXX_unrecognized)
+func (m *LinksResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
 	}
-	return n
+	return dAtA[:n], nil
 }
 
-func (m *RevisionMetadataQuery) Size() (n int) {
-	if m == nil {
-		return 0
-	}
+func (m *LinksResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *LinksResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
 	var l int
 	_ = l
-	if m.Name != nil {
-		l = len(*m.Name)
-		n += 1 + l + sovApplication(uint64(l))
-	}
-	if m.Revision != nil {
+	if m.XXX_unrecognized != nil {
+		i -= len(m.XXX_unrecognized)
+		copy(dAtA[i:], m.XXX_unrecognized)
+	}
+	if len(m.Items) > 0 {
+		for iNdEx := len(m.Items) - 1; iNdEx >= 0; iNdEx-- {
+			{
+				size, err := m.Items[iNdEx].MarshalToSizedBuffer(dAtA[:i])
+				if err != nil {
+					return 0, err
+				}
+				i -= size
+				i = encodeVarintApplication(dAtA, i, uint64(size))
+			}
+			i--
+			dAtA[i] = 0xa
+		}
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *ListAppLinksRequest) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *ListAppLinksRequest) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *ListAppLinksRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.XXX_unrecognized != nil {
+		i -= len(m.XXX_unrecognized)
+		copy(dAtA[i:], m.XXX_unrecognized)
+	}
+	if m.Namespace != nil {
+		i -= len(*m.Namespace)
+		copy(dAtA[i:], *m.Namespace)
+		i = encodeVarintApplication(dAtA, i, uint64(len(*m.Namespace)))
+		i--
+		dAtA[i] = 0x1a
+	}
+	if m.Name == nil {
+		return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("name")
+	} else {
+		i -= len(*m.Name)
+		copy(dAtA[i:], *m.Name)
+		i = encodeVarintApplication(dAtA, i, uint64(len(*m.Name)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintApplication(dAtA []byte, offset int, v uint64) int {
+	offset -= sovApplication(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *ApplicationQuery) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.Name != nil {
+		l = len(*m.Name)
+		n += 1 + l + sovApplication(uint64(l))
+	}
+	if m.Refresh != nil {
+		l = len(*m.Refresh)
+		n += 1 + l + sovApplication(uint64(l))
+	}
+	if len(m.Projects) > 0 {
+		for _, s := range m.Projects {
+			l = len(s)
+			n += 1 + l + sovApplication(uint64(l))
+		}
+	}
+	if m.ResourceVersion != nil {
+		l = len(*m.ResourceVersion)
+		n += 1 + l + sovApplication(uint64(l))
+	}
+	if m.Selector != nil {
+		l = len(*m.Selector)
+		n += 1 + l + sovApplication(uint64(l))
+	}
+	if m.Repo != nil {
+		l = len(*m.Repo)
+		n += 1 + l + sovApplication(uint64(l))
+	}
+	if m.AppNamespace != nil {
+		l = len(*m.AppNamespace)
+		n += 1 + l + sovApplication(uint64(l))
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+}
+
+func (m *NodeQuery) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.Name != nil {
+		l = len(*m.Name)
+		n += 1 + l + sovApplication(uint64(l))
+	}
+	if m.AppNamespace != nil {
+		l = len(*m.AppNamespace)
+		n += 1 + l + sovApplication(uint64(l))
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+}
+
+func (m *RevisionMetadataQuery) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.Name != nil {
+		l = len(*m.Name)
+		n += 1 + l + sovApplication(uint64(l))
+	}
+	if m.Revision != nil {
 		l = len(*m.Revision)
 		n += 1 + l + sovApplication(uint64(l))
 	}
@@ -6739,6 +7143,72 @@ func (m *ManagedResourcesResponse) Size() (n int) {
 	return n
 }
 
+func (m *LinkInfo) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.Title != nil {
+		l = len(*m.Title)
+		n += 1 + l + sovApplication(uint64(l))
+	}
+	if m.Url != nil {
+		l = len(*m.Url)
+		n += 1 + l + sovApplication(uint64(l))
+	}
+	if m.Description != nil {
+		l = len(*m.Description)
+		n += 1 + l + sovApplication(uint64(l))
+	}
+	if m.IconClass != nil {
+		l = len(*m.IconClass)
+		n += 1 + l + sovApplication(uint64(l))
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+}
+
+func (m *LinksResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if len(m.Items) > 0 {
+		for _, e := range m.Items {
+			l = e.Size()
+			n += 1 + l + sovApplication(uint64(l))
+		}
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+}
+
+func (m *ListAppLinksRequest) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.Name != nil {
+		l = len(*m.Name)
+		n += 1 + l + sovApplication(uint64(l))
+	}
+	if m.Namespace != nil {
+		l = len(*m.Namespace)
+		n += 1 + l + sovApplication(uint64(l))
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+}
+
 func sovApplication(x uint64) (n int) {
 	return (math_bits.Len64(x|1) + 6) / 7
 }
@@ -12832,6 +13302,405 @@ func (m *ManagedResourcesResponse) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
+func (m *LinkInfo) Unmarshal(dAtA []byte) error {
+	var hasFields [1]uint64
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowApplication
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: LinkInfo: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: LinkInfo: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowApplication
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthApplication
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthApplication
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			s := string(dAtA[iNdEx:postIndex])
+			m.Title = &s
+			iNdEx = postIndex
+			hasFields[0] |= uint64(0x00000001)
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Url", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowApplication
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthApplication
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthApplication
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			s := string(dAtA[iNdEx:postIndex])
+			m.Url = &s
+			iNdEx = postIndex
+			hasFields[0] |= uint64(0x00000002)
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowApplication
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthApplication
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthApplication
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			s := string(dAtA[iNdEx:postIndex])
+			m.Description = &s
+			iNdEx = postIndex
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field IconClass", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowApplication
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthApplication
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthApplication
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			s := string(dAtA[iNdEx:postIndex])
+			m.IconClass = &s
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipApplication(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthApplication
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+	if hasFields[0]&uint64(0x00000001) == 0 {
+		return github_com_gogo_protobuf_proto.NewRequiredNotSetError("title")
+	}
+	if hasFields[0]&uint64(0x00000002) == 0 {
+		return github_com_gogo_protobuf_proto.NewRequiredNotSetError("url")
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *LinksResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowApplication
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: LinksResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: LinksResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowApplication
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthApplication
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthApplication
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Items = append(m.Items, &LinkInfo{})
+			if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipApplication(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthApplication
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *ListAppLinksRequest) Unmarshal(dAtA []byte) error {
+	var hasFields [1]uint64
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowApplication
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: ListAppLinksRequest: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: ListAppLinksRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowApplication
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthApplication
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthApplication
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			s := string(dAtA[iNdEx:postIndex])
+			m.Name = &s
+			iNdEx = postIndex
+			hasFields[0] |= uint64(0x00000001)
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowApplication
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthApplication
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthApplication
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			s := string(dAtA[iNdEx:postIndex])
+			m.Namespace = &s
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipApplication(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthApplication
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+	if hasFields[0]&uint64(0x00000001) == 0 {
+		return github_com_gogo_protobuf_proto.NewRequiredNotSetError("name")
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
 func skipApplication(dAtA []byte) (n int, err error) {
 	l := len(dAtA)
 	iNdEx := 0
diff --git a/pkg/apiclient/application/application.pb.gw.go b/pkg/apiclient/application/application.pb.gw.go
index 56a1bbc27..80c86e4fc 100644
--- a/pkg/apiclient/application/application.pb.gw.go
+++ b/pkg/apiclient/application/application.pb.gw.go
@@ -1790,6 +1790,150 @@ func request_ApplicationService_PodLogs_1(ctx context.Context, marshaler runtime
 
 }
 
+var (
+	filter_ApplicationService_ListLinks_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
+)
+
+func request_ApplicationService_ListLinks_0(ctx context.Context, marshaler runtime.Marshaler, client ApplicationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq ListAppLinksRequest
+	var metadata runtime.ServerMetadata
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["name"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name")
+	}
+
+	protoReq.Name, err = runtime.StringP(val)
+
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
+	}
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ApplicationService_ListLinks_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.ListLinks(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_ApplicationService_ListLinks_0(ctx context.Context, marshaler runtime.Marshaler, server ApplicationServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq ListAppLinksRequest
+	var metadata runtime.ServerMetadata
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["name"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name")
+	}
+
+	protoReq.Name, err = runtime.StringP(val)
+
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
+	}
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ApplicationService_ListLinks_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.ListLinks(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+var (
+	filter_ApplicationService_ListResourceLinks_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
+)
+
+func request_ApplicationService_ListResourceLinks_0(ctx context.Context, marshaler runtime.Marshaler, client ApplicationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq ApplicationResourceRequest
+	var metadata runtime.ServerMetadata
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["name"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name")
+	}
+
+	protoReq.Name, err = runtime.StringP(val)
+
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
+	}
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ApplicationService_ListResourceLinks_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.ListResourceLinks(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_ApplicationService_ListResourceLinks_0(ctx context.Context, marshaler runtime.Marshaler, server ApplicationServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq ApplicationResourceRequest
+	var metadata runtime.ServerMetadata
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["name"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name")
+	}
+
+	protoReq.Name, err = runtime.StringP(val)
+
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
+	}
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ApplicationService_ListResourceLinks_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.ListResourceLinks(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
 // RegisterApplicationServiceHandlerServer registers the http handlers for service ApplicationService to "mux".
 // UnaryRPC     :call ApplicationServiceServer directly.
 // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
@@ -2314,6 +2458,52 @@ func RegisterApplicationServiceHandlerServer(ctx context.Context, mux *runtime.S
 		return
 	})
 
+	mux.Handle("GET", pattern_ApplicationService_ListLinks_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_ApplicationService_ListLinks_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_ApplicationService_ListLinks_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_ApplicationService_ListResourceLinks_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_ApplicationService_ListResourceLinks_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_ApplicationService_ListResourceLinks_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
 	return nil
 }
 
@@ -2875,6 +3065,46 @@ func RegisterApplicationServiceHandlerClient(ctx context.Context, mux *runtime.S
 
 	})
 
+	mux.Handle("GET", pattern_ApplicationService_ListLinks_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_ApplicationService_ListLinks_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_ApplicationService_ListLinks_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_ApplicationService_ListResourceLinks_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_ApplicationService_ListResourceLinks_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_ApplicationService_ListResourceLinks_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
 	return nil
 }
 
@@ -2930,6 +3160,10 @@ var (
 	pattern_ApplicationService_PodLogs_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6}, []string{"api", "v1", "applications", "name", "pods", "podName", "logs"}, "", runtime.AssumeColonVerbOpt(true)))
 
 	pattern_ApplicationService_PodLogs_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "applications", "name", "logs"}, "", runtime.AssumeColonVerbOpt(true)))
+
+	pattern_ApplicationService_ListLinks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "applications", "name", "links"}, "", runtime.AssumeColonVerbOpt(true)))
+
+	pattern_ApplicationService_ListResourceLinks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4, 2, 5}, []string{"api", "v1", "applications", "name", "resource", "links"}, "", runtime.AssumeColonVerbOpt(true)))
 )
 
 var (
@@ -2984,4 +3218,8 @@ var (
 	forward_ApplicationService_PodLogs_0 = runtime.ForwardResponseStream
 
 	forward_ApplicationService_PodLogs_1 = runtime.ForwardResponseStream
+
+	forward_ApplicationService_ListLinks_0 = runtime.ForwardResponseMessage
+
+	forward_ApplicationService_ListResourceLinks_0 = runtime.ForwardResponseMessage
 )
diff --git a/pkg/apiclient/project/project.pb.go b/pkg/apiclient/project/project.pb.go
index 97199cdd0..2a89c8c2a 100644
--- a/pkg/apiclient/project/project.pb.go
+++ b/pkg/apiclient/project/project.pb.go
@@ -10,6 +10,7 @@ package project
 import (
 	context "context"
 	fmt "fmt"
+	application "github.com/argoproj/argo-cd/v2/pkg/apiclient/application"
 	v1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
 	proto "github.com/gogo/protobuf/proto"
 	_ "google.golang.org/genproto/googleapis/api/annotations"
@@ -636,6 +637,53 @@ func (m *DetailedProjectsResponse) GetClusters() []*v1alpha1.Cluster {
 	return nil
 }
 
+type ListProjectLinksRequest struct {
+	Name                 string   `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *ListProjectLinksRequest) Reset()         { *m = ListProjectLinksRequest{} }
+func (m *ListProjectLinksRequest) String() string { return proto.CompactTextString(m) }
+func (*ListProjectLinksRequest) ProtoMessage()    {}
+func (*ListProjectLinksRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_5f0a51496972c9e2, []int{11}
+}
+func (m *ListProjectLinksRequest) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *ListProjectLinksRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_ListProjectLinksRequest.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *ListProjectLinksRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ListProjectLinksRequest.Merge(m, src)
+}
+func (m *ListProjectLinksRequest) XXX_Size() int {
+	return m.Size()
+}
+func (m *ListProjectLinksRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_ListProjectLinksRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ListProjectLinksRequest proto.InternalMessageInfo
+
+func (m *ListProjectLinksRequest) GetName() string {
+	if m != nil {
+		return m.Name
+	}
+	return ""
+}
+
 func init() {
 	proto.RegisterType((*ProjectCreateRequest)(nil), "project.ProjectCreateRequest")
 	proto.RegisterType((*ProjectTokenDeleteRequest)(nil), "project.ProjectTokenDeleteRequest")
@@ -648,72 +696,76 @@ func init() {
 	proto.RegisterType((*SyncWindowsResponse)(nil), "project.SyncWindowsResponse")
 	proto.RegisterType((*GlobalProjectsResponse)(nil), "project.GlobalProjectsResponse")
 	proto.RegisterType((*DetailedProjectsResponse)(nil), "project.DetailedProjectsResponse")
+	proto.RegisterType((*ListProjectLinksRequest)(nil), "project.ListProjectLinksRequest")
 }
 
 func init() { proto.RegisterFile("server/project/project.proto", fileDescriptor_5f0a51496972c9e2) }
 
 var fileDescriptor_5f0a51496972c9e2 = []byte{
-	// 950 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x96, 0xcf, 0x6f, 0xe3, 0x44,
-	0x14, 0xc7, 0xe5, 0xa4, 0xcd, 0x6e, 0x27, 0x4b, 0x29, 0xb3, 0xbb, 0xc5, 0x0d, 0xdd, 0x6e, 0x18,
-	0x44, 0x15, 0x15, 0x3a, 0x56, 0x53, 0x90, 0x56, 0x70, 0x62, 0x77, 0xab, 0x80, 0xd4, 0x03, 0xb8,
-	0x20, 0x10, 0x07, 0xd0, 0xc4, 0x7e, 0xca, 0xce, 0xc6, 0xf1, 0x0c, 0x9e, 0x89, 0xb7, 0x21, 0xea,
-	0x05, 0x09, 0x90, 0x38, 0x70, 0x80, 0x13, 0xff, 0x00, 0x27, 0xfe, 0x09, 0x6e, 0x1c, 0x91, 0xf8,
-	0x07, 0x50, 0xc5, 0x1f, 0x82, 0x3c, 0xfe, 0x91, 0x38, 0xa9, 0x11, 0x68, 0xc3, 0x9e, 0x3c, 0x1e,
-	0x3f, 0xbf, 0xef, 0xe7, 0xbd, 0x99, 0x79, 0x6f, 0xd0, 0xae, 0x82, 0x28, 0x86, 0xc8, 0x91, 0x91,
-	0x78, 0x0c, 0x9e, 0xce, 0x9f, 0x54, 0x46, 0x42, 0x0b, 0x7c, 0x2d, 0x7b, 0x6d, 0xed, 0x0e, 0x84,
-	0x18, 0x04, 0xe0, 0x30, 0xc9, 0x1d, 0x16, 0x86, 0x42, 0x33, 0xcd, 0x45, 0xa8, 0x52, 0xb3, 0x16,
-	0x19, 0xde, 0x53, 0x94, 0x0b, 0xf3, 0xd5, 0x13, 0x11, 0x38, 0xf1, 0x91, 0x33, 0x80, 0x10, 0x22,
-	0xa6, 0xc1, 0xcf, 0x6c, 0x4e, 0x07, 0x5c, 0x3f, 0x1a, 0xf7, 0xa9, 0x27, 0x46, 0x0e, 0x8b, 0x06,
-	0x22, 0xf1, 0x6c, 0x06, 0x87, 0x9e, 0xef, 0xc4, 0x5d, 0x47, 0x0e, 0x07, 0xc9, 0xff, 0xca, 0x61,
-	0x52, 0x06, 0xdc, 0x33, 0xfe, 0x9d, 0xf8, 0x88, 0x05, 0xf2, 0x11, 0x5b, 0xf2, 0x46, 0x7e, 0xb0,
-	0xd0, 0xad, 0xf7, 0x53, 0xb6, 0x07, 0x11, 0x30, 0x0d, 0x2e, 0x7c, 0x31, 0x06, 0xa5, 0x71, 0x1f,
-	0xe5, 0xcc, 0xb6, 0xd5, 0xb6, 0x3a, 0xcd, 0xee, 0xbb, 0x74, 0x26, 0x4c, 0x73, 0x61, 0x33, 0xf8,
-	0xdc, 0xf3, 0x69, 0xdc, 0xa5, 0x72, 0x38, 0xa0, 0x89, 0x30, 0x9d, 0x13, 0xa6, 0xb9, 0x30, 0x7d,
-	0x47, 0xca, 0x4c, 0xc7, 0xcd, 0x1d, 0xe3, 0x6d, 0xd4, 0x18, 0x4b, 0x05, 0x91, 0xb6, 0x6b, 0x6d,
-	0xab, 0x73, 0xdd, 0xcd, 0xde, 0xc8, 0x10, 0xed, 0x64, 0xb6, 0x1f, 0x8a, 0x21, 0x84, 0x0f, 0x21,
-	0x80, 0x19, 0x98, 0x5d, 0x06, 0xdb, 0x98, 0xb9, 0xc3, 0x68, 0x2d, 0x12, 0x01, 0x18, 0x67, 0x1b,
-	0xae, 0x19, 0xe3, 0x2d, 0x54, 0xe7, 0x4c, 0xdb, 0xf5, 0xb6, 0xd5, 0xa9, 0xbb, 0xc9, 0x10, 0x6f,
-	0xa2, 0x1a, 0xf7, 0xed, 0x35, 0x63, 0x53, 0xe3, 0x3e, 0xf9, 0xc9, 0x2a, 0xab, 0x95, 0xd3, 0x50,
-	0xad, 0xd6, 0x46, 0x4d, 0x1f, 0x94, 0x17, 0x71, 0x99, 0x04, 0x9a, 0x89, 0xce, 0x4f, 0x15, 0x3c,
-	0xf5, 0x39, 0x9e, 0x5d, 0xb4, 0x01, 0xe7, 0x92, 0x47, 0xa0, 0xde, 0x0b, 0x0d, 0x44, 0xdd, 0x9d,
-	0x4d, 0x64, 0x6c, 0xeb, 0x05, 0xdb, 0xeb, 0xc5, 0xe2, 0x18, 0x34, 0x17, 0x94, 0x14, 0xa1, 0x02,
-	0x7c, 0x0b, 0xad, 0xeb, 0x64, 0x22, 0x63, 0x4a, 0x5f, 0x08, 0x41, 0x37, 0x32, 0xeb, 0x0f, 0xc6,
-	0x10, 0x4d, 0x12, 0xfd, 0x90, 0x8d, 0x20, 0x33, 0x32, 0x63, 0xf2, 0x65, 0xe1, 0xf1, 0x23, 0xe9,
-	0x3f, 0xdb, 0xe5, 0x26, 0xcf, 0xa3, 0xe7, 0x4e, 0x46, 0x52, 0x4f, 0xf2, 0x30, 0xc8, 0x3e, 0xda,
-	0x3a, 0x9b, 0x84, 0xde, 0xc7, 0x3c, 0xf4, 0xc5, 0x13, 0x55, 0x0d, 0x3d, 0x41, 0x37, 0xe7, 0xec,
-	0x8a, 0x2c, 0xf4, 0xd1, 0xb5, 0x27, 0xe9, 0x94, 0x6d, 0xb5, 0xeb, 0x4f, 0xcf, 0x3c, 0xd3, 0x70,
-	0x73, 0xc7, 0xe4, 0x1c, 0x6d, 0xf7, 0x02, 0xd1, 0x67, 0x41, 0x16, 0xcd, 0x4c, 0xfd, 0x33, 0xb4,
-	0xce, 0x35, 0x8c, 0x56, 0xa4, 0x3d, 0x97, 0xaf, 0xd4, 0x2d, 0xf9, 0xb5, 0x8e, 0xec, 0x87, 0xa0,
-	0x19, 0x0f, 0xc0, 0x5f, 0x12, 0x97, 0x68, 0x73, 0x50, 0xc2, 0x5a, 0x39, 0xc5, 0x82, 0xff, 0xf9,
-	0x0d, 0x52, 0xfb, 0xbf, 0xea, 0x41, 0x80, 0x6e, 0x44, 0x20, 0x85, 0xe2, 0x5a, 0x44, 0x1c, 0x94,
-	0x5d, 0x5f, 0x45, 0x4c, 0x6e, 0xee, 0x71, 0xe2, 0x96, 0xbc, 0x63, 0x86, 0xae, 0x7b, 0xc1, 0x58,
-	0x69, 0x88, 0x94, 0xbd, 0x66, 0x94, 0x4e, 0x9e, 0x4e, 0xe9, 0x41, 0xea, 0xcd, 0x2d, 0xdc, 0x76,
-	0x7f, 0x69, 0xa2, 0xcd, 0x2c, 0xca, 0x33, 0x88, 0x62, 0xee, 0x01, 0xfe, 0xce, 0x42, 0xcd, 0xb4,
-	0xc4, 0x98, 0x23, 0x8d, 0x09, 0xcd, 0x3b, 0x45, 0x65, 0x11, 0x6a, 0xdd, 0xb9, 0xd2, 0xa6, 0x38,
-	0x46, 0xf7, 0xbe, 0xfa, 0xe3, 0xaf, 0x1f, 0x6b, 0x5d, 0x72, 0x68, 0xfa, 0x46, 0x7c, 0x94, 0xf7,
-	0x1e, 0xe5, 0x4c, 0xb3, 0xd1, 0x85, 0x93, 0x14, 0x1f, 0xe5, 0x4c, 0x93, 0xc7, 0x85, 0x63, 0xca,
-	0xc5, 0x5b, 0xd6, 0x01, 0xfe, 0xc6, 0x42, 0xcd, 0xb4, 0xba, 0xfe, 0x13, 0x4c, 0xa9, 0xfe, 0xb6,
-	0xb6, 0x0b, 0x9b, 0xf2, 0x61, 0x7e, 0xdb, 0x50, 0xbc, 0x79, 0x70, 0xfc, 0x9f, 0x28, 0x9c, 0x29,
-	0x67, 0xfa, 0x02, 0x7f, 0x6f, 0xa1, 0x46, 0x1a, 0x33, 0x5e, 0x0a, 0xb6, 0x9c, 0x8b, 0x95, 0x6d,
-	0x3b, 0xf2, 0x92, 0x01, 0xbe, 0x4d, 0xb6, 0x16, 0x81, 0x93, 0xcc, 0x7c, 0x6d, 0xa1, 0xb5, 0x53,
-	0xae, 0x34, 0xbe, 0xbd, 0x88, 0x63, 0xca, 0x54, 0xeb, 0x74, 0x55, 0x18, 0x89, 0x08, 0xb1, 0x0d,
-	0x0a, 0xc6, 0x4b, 0x28, 0xf8, 0x1c, 0xe1, 0x1e, 0xe8, 0x85, 0x3a, 0x50, 0x05, 0xf5, 0x72, 0x31,
-	0x5d, 0x55, 0x38, 0x48, 0xc7, 0x28, 0x11, 0xdc, 0x5e, 0x5e, 0xa5, 0xa4, 0xd4, 0x5e, 0x38, 0x7e,
-	0xf6, 0x27, 0xfe, 0xd6, 0x42, 0xf5, 0x1e, 0x54, 0x6a, 0xad, 0x6e, 0x1d, 0xee, 0x1a, 0xa4, 0x1d,
-	0xfc, 0x62, 0x05, 0x12, 0x9e, 0xa2, 0x17, 0x7a, 0xa0, 0xcb, 0x65, 0xb8, 0x0a, 0xeb, 0x6e, 0x31,
-	0x7d, 0x75, 0xd9, 0x26, 0xd4, 0xa8, 0x75, 0xf0, 0x7e, 0x55, 0x02, 0xd2, 0xba, 0x57, 0x2c, 0xc0,
-	0xcf, 0x16, 0x6a, 0xa4, 0xad, 0x72, 0x79, 0x67, 0x96, 0x5a, 0xe8, 0x0a, 0x33, 0x72, 0x6c, 0x18,
-	0x0f, 0x5b, 0x9d, 0xca, 0xa3, 0x44, 0x47, 0xa0, 0x99, 0xcf, 0x34, 0xa3, 0x06, 0x3a, 0xd9, 0xb1,
-	0x9f, 0xa0, 0x46, 0x7a, 0x50, 0xab, 0x52, 0x53, 0x75, 0x70, 0xb3, 0xfc, 0x1f, 0x54, 0xe6, 0xff,
-	0x31, 0x42, 0xc9, 0x2e, 0x3d, 0x89, 0x21, 0xac, 0x4e, 0xfc, 0x1d, 0x9a, 0xde, 0x5d, 0x93, 0x08,
-	0x69, 0x72, 0x77, 0xa5, 0xf1, 0x11, 0x35, 0xbf, 0x98, 0x1d, 0xbe, 0x6f, 0x44, 0xda, 0x78, 0xaf,
-	0x2a, 0xed, 0x90, 0x7a, 0x9f, 0xa2, 0x9b, 0x3d, 0xd0, 0x73, 0xdd, 0xfe, 0x4c, 0x27, 0xa9, 0xdf,
-	0x29, 0x44, 0x17, 0x2f, 0x0c, 0xad, 0xdd, 0xab, 0x3e, 0x15, 0xc1, 0xbd, 0x66, 0x74, 0x5f, 0xc5,
-	0xaf, 0x54, 0xe9, 0xaa, 0x49, 0xe8, 0x65, 0xcd, 0xfe, 0xfe, 0xfd, 0xdf, 0x2e, 0xf7, 0xac, 0xdf,
-	0x2f, 0xf7, 0xac, 0x3f, 0x2f, 0xf7, 0xac, 0x4f, 0xdf, 0xf8, 0x77, 0x17, 0x6d, 0x2f, 0xe0, 0x10,
-	0x16, 0xf7, 0xfd, 0x7e, 0xc3, 0xdc, 0xab, 0x8f, 0xff, 0x0e, 0x00, 0x00, 0xff, 0xff, 0x0c, 0x1d,
-	0x2d, 0x85, 0x10, 0x0c, 0x00, 0x00,
+	// 1007 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xcf, 0x6f, 0xe3, 0x44,
+	0x14, 0x96, 0x93, 0x36, 0xbb, 0x7d, 0x2d, 0xa5, 0xcc, 0xee, 0x76, 0xdd, 0xd0, 0x1f, 0x61, 0xd0,
+	0x56, 0x51, 0xa1, 0xb6, 0x9a, 0x82, 0xb4, 0x82, 0x13, 0xdb, 0xad, 0x02, 0x52, 0x0f, 0xe0, 0x82,
+	0x40, 0x1c, 0x40, 0x8e, 0xfd, 0x94, 0x9d, 0x8d, 0x63, 0x1b, 0xcf, 0x24, 0xdb, 0x10, 0xf5, 0x82,
+	0x04, 0x48, 0x1c, 0x38, 0xc0, 0x89, 0x0b, 0x47, 0xfe, 0x0f, 0x6e, 0x1c, 0x91, 0xf8, 0x07, 0x50,
+	0xc5, 0x1f, 0x82, 0x66, 0x3c, 0x76, 0xe2, 0xa4, 0xe6, 0x87, 0x1a, 0x38, 0x65, 0x3c, 0x7e, 0xfe,
+	0xbe, 0xef, 0xbd, 0x79, 0xf3, 0xcd, 0x04, 0xb6, 0x39, 0x26, 0x43, 0x4c, 0xec, 0x38, 0x89, 0x9e,
+	0xa2, 0x27, 0xb2, 0x5f, 0x2b, 0x4e, 0x22, 0x11, 0x91, 0x5b, 0xfa, 0xb1, 0xbe, 0xdd, 0x8d, 0xa2,
+	0x6e, 0x80, 0xb6, 0x1b, 0x33, 0xdb, 0x0d, 0xc3, 0x48, 0xb8, 0x82, 0x45, 0x21, 0x4f, 0xc3, 0xea,
+	0xb4, 0xf7, 0x90, 0x5b, 0x2c, 0x52, 0x6f, 0xbd, 0x28, 0x41, 0x7b, 0x78, 0x64, 0x77, 0x31, 0xc4,
+	0xc4, 0x15, 0xe8, 0xeb, 0x98, 0xb3, 0x2e, 0x13, 0x4f, 0x06, 0x1d, 0xcb, 0x8b, 0xfa, 0xb6, 0x9b,
+	0x74, 0x23, 0x89, 0xac, 0x06, 0x87, 0x9e, 0x6f, 0x0f, 0x5b, 0x76, 0xdc, 0xeb, 0xca, 0xef, 0xb9,
+	0xed, 0xc6, 0x71, 0xc0, 0x3c, 0x85, 0x6f, 0x0f, 0x8f, 0xdc, 0x20, 0x7e, 0xe2, 0xce, 0xa3, 0x9d,
+	0xfc, 0x0d, 0x9a, 0xce, 0x6a, 0x1a, 0x6b, 0x6a, 0x9c, 0x82, 0xd0, 0xef, 0x0c, 0xb8, 0xfb, 0x6e,
+	0x9a, 0xe0, 0x49, 0x82, 0xae, 0x40, 0x07, 0x3f, 0x1b, 0x20, 0x17, 0xa4, 0x03, 0x59, 0xe2, 0xa6,
+	0xd1, 0x30, 0x9a, 0xab, 0xad, 0xb7, 0xad, 0x09, 0x9f, 0x95, 0xf1, 0xa9, 0xc1, 0xa7, 0x9e, 0x6f,
+	0x0d, 0x5b, 0x56, 0xdc, 0xeb, 0x5a, 0x52, 0xbd, 0x35, 0xcd, 0x92, 0xa9, 0xb7, 0xde, 0x8a, 0x63,
+	0xcd, 0xe3, 0x64, 0xc0, 0x64, 0x13, 0x6a, 0x83, 0x98, 0x63, 0x22, 0xcc, 0x4a, 0xc3, 0x68, 0xde,
+	0x76, 0xf4, 0x13, 0xed, 0xc1, 0x96, 0x8e, 0x7d, 0x3f, 0xea, 0x61, 0xf8, 0x18, 0x03, 0x9c, 0x08,
+	0x33, 0x8b, 0xc2, 0x56, 0x26, 0x70, 0x04, 0x96, 0x92, 0x28, 0x40, 0x05, 0xb6, 0xe2, 0xa8, 0x31,
+	0xd9, 0x80, 0x2a, 0x73, 0x85, 0x59, 0x6d, 0x18, 0xcd, 0xaa, 0x23, 0x87, 0x64, 0x1d, 0x2a, 0xcc,
+	0x37, 0x97, 0x54, 0x4c, 0x85, 0xf9, 0xf4, 0x07, 0xa3, 0xc8, 0x56, 0x2c, 0x43, 0x39, 0x5b, 0x03,
+	0x56, 0x7d, 0xe4, 0x5e, 0xc2, 0x62, 0x99, 0xa8, 0x26, 0x9d, 0x9e, 0xca, 0xf5, 0x54, 0xa7, 0xf4,
+	0x6c, 0xc3, 0x0a, 0x5e, 0xc4, 0x2c, 0x41, 0xfe, 0x4e, 0xa8, 0x44, 0x54, 0x9d, 0xc9, 0x84, 0xd6,
+	0xb6, 0x9c, 0x6b, 0x7b, 0x35, 0x5f, 0x1c, 0x25, 0xcd, 0x41, 0x1e, 0x47, 0x21, 0x47, 0x72, 0x17,
+	0x96, 0x85, 0x9c, 0xd0, 0x9a, 0xd2, 0x07, 0x4a, 0x61, 0x4d, 0x47, 0xbf, 0x37, 0xc0, 0x64, 0x24,
+	0xf9, 0x43, 0xb7, 0x8f, 0x3a, 0x48, 0x8d, 0xe9, 0xe7, 0x39, 0xe2, 0x07, 0xb1, 0xff, 0xff, 0x2e,
+	0x37, 0x7d, 0x1e, 0x9e, 0x3b, 0xed, 0xc7, 0x62, 0x94, 0xa5, 0x41, 0xf7, 0x61, 0xe3, 0x7c, 0x14,
+	0x7a, 0x1f, 0xb2, 0xd0, 0x8f, 0x9e, 0xf1, 0x72, 0xd1, 0x23, 0xb8, 0x33, 0x15, 0x97, 0x57, 0xa1,
+	0x03, 0xb7, 0x9e, 0xa5, 0x53, 0xa6, 0xd1, 0xa8, 0xde, 0x5c, 0xf3, 0x84, 0xc3, 0xc9, 0x80, 0xe9,
+	0x05, 0x6c, 0xb6, 0x83, 0xa8, 0xe3, 0x06, 0x3a, 0x9b, 0x09, 0xfb, 0x27, 0xb0, 0xcc, 0x04, 0xf6,
+	0x17, 0xc4, 0x3d, 0x55, 0xaf, 0x14, 0x96, 0xfe, 0x5c, 0x05, 0xf3, 0x31, 0x0a, 0x97, 0x05, 0xe8,
+	0xcf, 0x91, 0xc7, 0xb0, 0xde, 0x2d, 0xc8, 0x5a, 0xb8, 0x8a, 0x19, 0xfc, 0xe9, 0x06, 0xa9, 0xfc,
+	0x57, 0x7e, 0x10, 0xc0, 0x5a, 0x82, 0x71, 0xc4, 0x99, 0x88, 0x12, 0x86, 0xdc, 0xac, 0x2e, 0x22,
+	0x27, 0x27, 0x43, 0x1c, 0x39, 0x05, 0x74, 0xe2, 0xc2, 0x6d, 0x2f, 0x18, 0x70, 0x81, 0x09, 0x37,
+	0x97, 0x14, 0xd3, 0xe9, 0xcd, 0x98, 0x4e, 0x52, 0x34, 0x27, 0x87, 0xa5, 0x87, 0x70, 0xff, 0x8c,
+	0x71, 0xa1, 0x13, 0x3d, 0x63, 0x61, 0x8f, 0x67, 0x1b, 0xee, 0x9a, 0x3e, 0x6f, 0xfd, 0xb8, 0x06,
+	0xeb, 0x3a, 0xf6, 0x1c, 0x93, 0x21, 0xf3, 0x90, 0x7c, 0x63, 0xc0, 0x6a, 0xea, 0x48, 0xca, 0x01,
+	0x08, 0xb5, 0xb2, 0xd3, 0xa9, 0xd4, 0xb3, 0xea, 0x3b, 0xd7, 0xc6, 0xe4, 0xbb, 0xee, 0xe1, 0x17,
+	0xbf, 0xfd, 0xf1, 0x7d, 0xa5, 0x45, 0x0f, 0xd5, 0x59, 0x35, 0x3c, 0xca, 0xce, 0x3b, 0x6e, 0x8f,
+	0xf5, 0xe8, 0xd2, 0x96, 0x5e, 0xc5, 0xed, 0xb1, 0xfc, 0xb9, 0xb4, 0x95, 0xbb, 0xbc, 0x61, 0x1c,
+	0x90, 0xaf, 0x0c, 0x58, 0x4d, 0xcd, 0xf8, 0xaf, 0xc4, 0x14, 0xec, 0xba, 0xbe, 0x99, 0xc7, 0x14,
+	0xf7, 0xfe, 0x9b, 0x4a, 0xc5, 0xeb, 0x07, 0xc7, 0xff, 0x4a, 0x85, 0x3d, 0x66, 0xae, 0xb8, 0x24,
+	0xdf, 0x1a, 0x50, 0x4b, 0x73, 0x26, 0x73, 0xc9, 0x16, 0x6b, 0xb1, 0xb0, 0x2e, 0xa5, 0x2f, 0x2a,
+	0xc1, 0xf7, 0xe8, 0xc6, 0xac, 0x60, 0x59, 0x99, 0x2f, 0x0d, 0x58, 0x92, 0x2b, 0x4d, 0xee, 0xcd,
+	0xca, 0x51, 0xae, 0x56, 0x3f, 0x5b, 0x94, 0x0c, 0x49, 0x42, 0x4d, 0x25, 0x85, 0x90, 0x39, 0x29,
+	0xe4, 0x02, 0x48, 0x1b, 0xc5, 0x8c, 0x6d, 0x94, 0x89, 0x7a, 0x29, 0x9f, 0x2e, 0xf3, 0x19, 0xda,
+	0x54, 0x4c, 0x94, 0x34, 0xe6, 0x57, 0x49, 0x76, 0xec, 0xa5, 0xed, 0xeb, 0x2f, 0xc9, 0xd7, 0x06,
+	0x54, 0xdb, 0x58, 0xca, 0xb5, 0xb8, 0x75, 0xd8, 0x53, 0x92, 0xb6, 0xc8, 0xfd, 0x12, 0x49, 0x64,
+	0x0c, 0x2f, 0xb4, 0x51, 0x14, 0x5d, 0xbb, 0x4c, 0xd6, 0x5e, 0x3e, 0x7d, 0xbd, 0xcb, 0x53, 0x4b,
+	0xb1, 0x35, 0xc9, 0x7e, 0x59, 0x01, 0x52, 0x9b, 0xcc, 0x17, 0xe0, 0x27, 0x03, 0x6a, 0xe9, 0xc9,
+	0x3a, 0xdf, 0x99, 0x85, 0x13, 0x77, 0x81, 0x15, 0x39, 0x56, 0x1a, 0x0f, 0xeb, 0xcd, 0xd2, 0xad,
+	0x64, 0xf5, 0x51, 0xb8, 0xbe, 0x2b, 0x5c, 0x4b, 0x89, 0x96, 0x1d, 0xfb, 0x11, 0xd4, 0xd2, 0x8d,
+	0x5a, 0x56, 0x9a, 0xb2, 0x8d, 0xab, 0xeb, 0x7f, 0x50, 0x5a, 0xff, 0xa7, 0x00, 0xb2, 0x4b, 0x4f,
+	0x87, 0x18, 0x96, 0x17, 0x7e, 0xc7, 0x4a, 0xef, 0xcb, 0x32, 0x43, 0x4b, 0xde, 0x97, 0xad, 0xe1,
+	0x91, 0xa5, 0x3e, 0x51, 0x1d, 0xbe, 0xaf, 0x48, 0x1a, 0x64, 0xb7, 0xac, 0xec, 0x98, 0xa2, 0x8f,
+	0xe1, 0x4e, 0x1b, 0xc5, 0xd4, 0xe5, 0xe0, 0x5c, 0xc8, 0xd2, 0x6f, 0xe5, 0xa4, 0xb3, 0xf7, 0x8b,
+	0xfa, 0xf6, 0x75, 0xaf, 0xf2, 0xe4, 0x5e, 0x51, 0xbc, 0x0f, 0xc8, 0xcb, 0x65, 0xbc, 0x7c, 0x14,
+	0x7a, 0xfa, 0x6e, 0x40, 0x62, 0x58, 0x91, 0x62, 0x95, 0xad, 0x93, 0x46, 0x8e, 0x5b, 0xe2, 0xf8,
+	0xf5, 0x7a, 0x61, 0x21, 0xf5, 0x2b, 0xcd, 0xfb, 0x40, 0xf1, 0xee, 0x91, 0x9d, 0x32, 0xde, 0x40,
+	0x86, 0x3f, 0x7a, 0xf4, 0xcb, 0xd5, 0xae, 0xf1, 0xeb, 0xd5, 0xae, 0xf1, 0xfb, 0xd5, 0xae, 0xf1,
+	0xf1, 0x6b, 0xff, 0xec, 0xef, 0x84, 0x17, 0x30, 0x0c, 0xf3, 0x7f, 0x35, 0x9d, 0x9a, 0xba, 0xf8,
+	0x1f, 0xff, 0x19, 0x00, 0x00, 0xff, 0xff, 0x28, 0x9d, 0xea, 0xa8, 0xf6, 0x0c, 0x00, 0x00,
 }
 
 // Reference imports to suppress errors if they are not otherwise used.
@@ -750,6 +802,8 @@ type ProjectServiceClient interface {
 	ListEvents(ctx context.Context, in *ProjectQuery, opts ...grpc.CallOption) (*v1.EventList, error)
 	// GetSchedulesState returns true if there are any active sync syncWindows
 	GetSyncWindowsState(ctx context.Context, in *SyncWindowsQuery, opts ...grpc.CallOption) (*SyncWindowsResponse, error)
+	// ListLinks returns all deep links for the particular project
+	ListLinks(ctx context.Context, in *ListProjectLinksRequest, opts ...grpc.CallOption) (*application.LinksResponse, error)
 }
 
 type projectServiceClient struct {
@@ -859,6 +913,15 @@ func (c *projectServiceClient) GetSyncWindowsState(ctx context.Context, in *Sync
 	return out, nil
 }
 
+func (c *projectServiceClient) ListLinks(ctx context.Context, in *ListProjectLinksRequest, opts ...grpc.CallOption) (*application.LinksResponse, error) {
+	out := new(application.LinksResponse)
+	err := c.cc.Invoke(ctx, "/project.ProjectService/ListLinks", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
 // ProjectServiceServer is the server API for ProjectService service.
 type ProjectServiceServer interface {
 	// Create a new project token
@@ -883,6 +946,8 @@ type ProjectServiceServer interface {
 	ListEvents(context.Context, *ProjectQuery) (*v1.EventList, error)
 	// GetSchedulesState returns true if there are any active sync syncWindows
 	GetSyncWindowsState(context.Context, *SyncWindowsQuery) (*SyncWindowsResponse, error)
+	// ListLinks returns all deep links for the particular project
+	ListLinks(context.Context, *ListProjectLinksRequest) (*application.LinksResponse, error)
 }
 
 // UnimplementedProjectServiceServer can be embedded to have forward compatible implementations.
@@ -922,6 +987,9 @@ func (*UnimplementedProjectServiceServer) ListEvents(ctx context.Context, req *P
 func (*UnimplementedProjectServiceServer) GetSyncWindowsState(ctx context.Context, req *SyncWindowsQuery) (*SyncWindowsResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method GetSyncWindowsState not implemented")
 }
+func (*UnimplementedProjectServiceServer) ListLinks(ctx context.Context, req *ListProjectLinksRequest) (*application.LinksResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method ListLinks not implemented")
+}
 
 func RegisterProjectServiceServer(s *grpc.Server, srv ProjectServiceServer) {
 	s.RegisterService(&_ProjectService_serviceDesc, srv)
@@ -1125,6 +1193,24 @@ func _ProjectService_GetSyncWindowsState_Handler(srv interface{}, ctx context.Co
 	return interceptor(ctx, in, info, handler)
 }
 
+func _ProjectService_ListLinks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(ListProjectLinksRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(ProjectServiceServer).ListLinks(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/project.ProjectService/ListLinks",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(ProjectServiceServer).ListLinks(ctx, req.(*ListProjectLinksRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 var _ProjectService_serviceDesc = grpc.ServiceDesc{
 	ServiceName: "project.ProjectService",
 	HandlerType: (*ProjectServiceServer)(nil),
@@ -1173,6 +1259,10 @@ var _ProjectService_serviceDesc = grpc.ServiceDesc{
 			MethodName: "GetSyncWindowsState",
 			Handler:    _ProjectService_GetSyncWindowsState_Handler,
 		},
+		{
+			MethodName: "ListLinks",
+			Handler:    _ProjectService_ListLinks_Handler,
+		},
 	},
 	Streams:  []grpc.StreamDesc{},
 	Metadata: "server/project/project.proto",
@@ -1671,6 +1761,40 @@ func (m *DetailedProjectsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error
 	return len(dAtA) - i, nil
 }
 
+func (m *ListProjectLinksRequest) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *ListProjectLinksRequest) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *ListProjectLinksRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.XXX_unrecognized != nil {
+		i -= len(m.XXX_unrecognized)
+		copy(dAtA[i:], m.XXX_unrecognized)
+	}
+	if len(m.Name) > 0 {
+		i -= len(m.Name)
+		copy(dAtA[i:], m.Name)
+		i = encodeVarintProject(dAtA, i, uint64(len(m.Name)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
 func encodeVarintProject(dAtA []byte, offset int, v uint64) int {
 	offset -= sovProject(v)
 	base := offset
@@ -1905,6 +2029,22 @@ func (m *DetailedProjectsResponse) Size() (n int) {
 	return n
 }
 
+func (m *ListProjectLinksRequest) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Name)
+	if l > 0 {
+		n += 1 + l + sovProject(uint64(l))
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+}
+
 func sovProject(x uint64) (n int) {
 	return (math_bits.Len64(x|1) + 6) / 7
 }
@@ -3128,6 +3268,89 @@ func (m *DetailedProjectsResponse) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
+func (m *ListProjectLinksRequest) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowProject
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: ListProjectLinksRequest: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: ListProjectLinksRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowProject
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthProject
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthProject
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Name = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipProject(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthProject
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
 func skipProject(dAtA []byte) (n int, err error) {
 	l := len(dAtA)
 	iNdEx := 0
diff --git a/pkg/apiclient/project/project.pb.gw.go b/pkg/apiclient/project/project.pb.gw.go
index 8ae2e95a6..f672aa23c 100644
--- a/pkg/apiclient/project/project.pb.gw.go
+++ b/pkg/apiclient/project/project.pb.gw.go
@@ -705,6 +705,60 @@ func local_request_ProjectService_GetSyncWindowsState_0(ctx context.Context, mar
 
 }
 
+func request_ProjectService_ListLinks_0(ctx context.Context, marshaler runtime.Marshaler, client ProjectServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq ListProjectLinksRequest
+	var metadata runtime.ServerMetadata
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["name"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name")
+	}
+
+	protoReq.Name, err = runtime.String(val)
+
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
+	}
+
+	msg, err := client.ListLinks(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_ProjectService_ListLinks_0(ctx context.Context, marshaler runtime.Marshaler, server ProjectServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq ListProjectLinksRequest
+	var metadata runtime.ServerMetadata
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["name"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name")
+	}
+
+	protoReq.Name, err = runtime.String(val)
+
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
+	}
+
+	msg, err := server.ListLinks(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
 // RegisterProjectServiceHandlerServer registers the http handlers for service ProjectService to "mux".
 // UnaryRPC     :call ProjectServiceServer directly.
 // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
@@ -964,6 +1018,29 @@ func RegisterProjectServiceHandlerServer(ctx context.Context, mux *runtime.Serve
 
 	})
 
+	mux.Handle("GET", pattern_ProjectService_ListLinks_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_ProjectService_ListLinks_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_ProjectService_ListLinks_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
 	return nil
 }
 
@@ -1225,6 +1302,26 @@ func RegisterProjectServiceHandlerClient(ctx context.Context, mux *runtime.Serve
 
 	})
 
+	mux.Handle("GET", pattern_ProjectService_ListLinks_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_ProjectService_ListLinks_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_ProjectService_ListLinks_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
 	return nil
 }
 
@@ -1250,6 +1347,8 @@ var (
 	pattern_ProjectService_ListEvents_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "projects", "name", "events"}, "", runtime.AssumeColonVerbOpt(true)))
 
 	pattern_ProjectService_GetSyncWindowsState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "projects", "name", "syncwindows"}, "", runtime.AssumeColonVerbOpt(true)))
+
+	pattern_ProjectService_ListLinks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "projects", "name", "links"}, "", runtime.AssumeColonVerbOpt(true)))
 )
 
 var (
@@ -1274,4 +1373,6 @@ var (
 	forward_ProjectService_ListEvents_0 = runtime.ForwardResponseMessage
 
 	forward_ProjectService_GetSyncWindowsState_0 = runtime.ForwardResponseMessage
+
+	forward_ProjectService_ListLinks_0 = runtime.ForwardResponseMessage
 )
diff --git a/reposerver/repository/repository.go b/reposerver/repository/repository.go
index ca2059ae8..80d81f42b 100644
--- a/reposerver/repository/repository.go
+++ b/reposerver/repository/repository.go
@@ -818,6 +818,32 @@ func runHelmBuild(appPath string, h helm.Helm) error {
 	return os.WriteFile(markerFile, []byte("marker"), 0644)
 }
 
+func populateRequestRepos(appPath string, q *apiclient.ManifestRequest) error {
+	repos, err := getHelmDependencyRepos(appPath)
+	if err != nil {
+		return err
+	}
+
+	for _, r := range repos {
+		if !repoExists(r.Repo, q.Repos) {
+			repositoryCredential := getRepoCredential(q.HelmRepoCreds, r.Repo)
+			if repositoryCredential != nil {
+				if repositoryCredential.EnableOCI {
+					r.Repo = strings.TrimPrefix(r.Repo, ociPrefix)
+				}
+				r.EnableOCI = repositoryCredential.EnableOCI
+				r.Password = repositoryCredential.Password
+				r.Username = repositoryCredential.Username
+				r.SSHPrivateKey = repositoryCredential.SSHPrivateKey
+				r.TLSClientCertData = repositoryCredential.TLSClientCertData
+				r.TLSClientCertKey = repositoryCredential.TLSClientCertKey
+			}
+			q.Repos = append(q.Repos, r)
+		}
+	}
+	return nil
+}
+
 func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclient.ManifestRequest, isLocal bool) ([]*unstructured.Unstructured, error) {
 	concurrencyAllowed := isConcurrencyAllowed(appPath)
 	if !concurrencyAllowed {
@@ -914,24 +940,8 @@ func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclie
 		templateOpts.SetString[i] = env.Envsubst(j)
 	}
 
-	repos, err := getHelmDependencyRepos(appPath)
-	if err != nil {
-		return nil, err
-	}
-
-	for _, r := range repos {
-		if !repoExists(r.Repo, q.Repos) {
-			repositoryCredential := getRepoCredential(q.HelmRepoCreds, r.Repo)
-			if repositoryCredential != nil {
-				r.EnableOCI = repositoryCredential.EnableOCI
-				r.Password = repositoryCredential.Password
-				r.Username = repositoryCredential.Username
-				r.SSHPrivateKey = repositoryCredential.SSHPrivateKey
-				r.TLSClientCertData = repositoryCredential.TLSClientCertData
-				r.TLSClientCertKey = repositoryCredential.TLSClientCertKey
-			}
-			q.Repos = append(q.Repos, r)
-		}
+	if err := populateRequestRepos(appPath, q); err != nil {
+		return nil, fmt.Errorf("failed parsing dependencies: %v", err)
 	}
 
 	var proxy string
diff --git a/server/application/application.go b/server/application/application.go
index 686b971d0..b56db0941 100644
--- a/server/application/application.go
+++ b/server/application/application.go
@@ -42,6 +42,7 @@ import (
 	applisters "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
 	"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
 	servercache "github.com/argoproj/argo-cd/v2/server/cache"
+	"github.com/argoproj/argo-cd/v2/server/deeplinks"
 	"github.com/argoproj/argo-cd/v2/server/rbacpolicy"
 	"github.com/argoproj/argo-cd/v2/util/argo"
 	argoutil "github.com/argoproj/argo-cd/v2/util/argo"
@@ -1892,6 +1893,64 @@ func (s *Server) Rollback(ctx context.Context, rollbackReq *application.Applicat
 	return a, nil
 }
 
+func (s *Server) ListLinks(ctx context.Context, req *application.ListAppLinksRequest) (*application.LinksResponse, error) {
+	appName := req.GetName()
+	appNs := s.appNamespaceOrDefault(req.GetNamespace())
+
+	a, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{})
+	if err != nil {
+		log.WithFields(map[string]interface{}{
+			"application": appName,
+			"ns":          appNs,
+		}).Errorf("failed to get application, error=%v", err.Error())
+		return nil, fmt.Errorf("error getting application")
+	}
+
+	if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil {
+		log.WithFields(map[string]interface{}{
+			"application": appName,
+			"ns":          appNs,
+		}).Warnf("unauthorized access to app, error=%v", err.Error())
+		return nil, fmt.Errorf("error getting application")
+	}
+
+	obj, err := kube.ToUnstructured(a)
+	if err != nil {
+		return nil, fmt.Errorf("error getting application: %w", err)
+	}
+
+	deepLinks, err := s.settingsMgr.GetDeepLinks(settings.ApplicationDeepLinks)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read application deep links from configmap: %w", err)
+	}
+
+	finalList, errorList := deeplinks.EvaluateDeepLinksResponse(*obj, deepLinks)
+	if len(errorList) > 0 {
+		log.Errorf("errorList while evaluating application deep links, %v", strings.Join(errorList, ", "))
+	}
+
+	return finalList, nil
+}
+
+func (s *Server) ListResourceLinks(ctx context.Context, req *application.ApplicationResourceRequest) (*application.LinksResponse, error) {
+	obj, _, _, _, err := s.getUnstructuredLiveResourceOrApp(ctx, rbacpolicy.ActionGet, req)
+	if err != nil {
+		return nil, err
+	}
+
+	deepLinks, err := s.settingsMgr.GetDeepLinks(settings.ResourceDeepLinks)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read application deep links from configmap: %w", err)
+	}
+
+	finalList, errorList := deeplinks.EvaluateDeepLinksResponse(*obj, deepLinks)
+	if len(errorList) > 0 {
+		log.Errorf("errors while evaluating resource deep links, %v", strings.Join(errorList, ", "))
+	}
+
+	return finalList, nil
+}
+
 // resolveRevision resolves the revision specified either in the sync request, or the
 // application source, into a concrete revision that will be used for a sync operation.
 func (s *Server) resolveRevision(ctx context.Context, app *appv1.Application, syncReq *application.ApplicationSyncRequest) (string, string, error) {
diff --git a/server/application/application.proto b/server/application/application.proto
index 50c229015..4fb08a708 100644
--- a/server/application/application.proto
+++ b/server/application/application.proto
@@ -264,6 +264,23 @@ message ManagedResourcesResponse {
 	repeated github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ResourceDiff items = 1;
 }
 
+message LinkInfo {
+	required string title = 1;
+	required string url = 2;
+	optional string description = 3;
+	optional string iconClass = 4;
+}
+
+message LinksResponse {
+	repeated LinkInfo items = 1;
+}
+
+message ListAppLinksRequest {
+	required string name = 1;
+	optional string namespace = 3;
+}
+
+
 // ApplicationService
 service ApplicationService {
 
@@ -425,4 +442,14 @@ service ApplicationService {
 			}
 		};
 	}
+
+	// ListLinks returns the list of all application deep links
+	rpc ListLinks(ListAppLinksRequest) returns (LinksResponse) {
+		option (google.api.http).get = "/api/v1/applications/{name}/links";
+	}
+
+	// ListResourceLinks returns the list of all resource deep links
+	rpc ListResourceLinks(ApplicationResourceRequest) returns (LinksResponse) {
+		option (google.api.http).get = "/api/v1/applications/{name}/resource/links";
+	}
 }
diff --git a/server/deeplinks/deeplinks.go b/server/deeplinks/deeplinks.go
new file mode 100644
index 000000000..096521ab2
--- /dev/null
+++ b/server/deeplinks/deeplinks.go
@@ -0,0 +1,63 @@
+package deeplinks
+
+import (
+	"bytes"
+	"fmt"
+	"text/template"
+
+	"github.com/antonmedv/expr"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/utils/pointer"
+
+	"github.com/argoproj/argo-cd/v2/pkg/apiclient/application"
+	"github.com/argoproj/argo-cd/v2/util/settings"
+)
+
+func EvaluateDeepLinksResponse(obj unstructured.Unstructured, links []settings.DeepLink) (*application.LinksResponse, []string) {
+	finalLinks := []*application.LinkInfo{}
+	errors := []string{}
+	for _, link := range links {
+		t, err := template.New("deep-link").Parse(link.URL)
+		if err != nil {
+			errors = append(errors, fmt.Sprintf("failed to parse link template '%v', error=%v", link.URL, err.Error()))
+			continue
+		}
+		finalURL := bytes.Buffer{}
+		err = t.Execute(&finalURL, obj.Object)
+		if err != nil {
+			errors = append(errors, fmt.Sprintf("failed to evaluate link template '%v' with resource %v, error=%v", link.URL, obj.GetName(), err.Error()))
+			continue
+		}
+		if link.Condition != nil {
+			out, err := expr.Eval(*link.Condition, obj.Object)
+			if err != nil {
+				errors = append(errors, fmt.Sprintf("failed to evaluate link condition '%v' with resource %v, error=%v", *link.Condition, obj.GetName(), err.Error()))
+				continue
+			}
+			switch resOut := out.(type) {
+			case bool:
+				if resOut {
+					finalLinks = append(finalLinks, &application.LinkInfo{
+						Title:       pointer.String(link.Title),
+						Url:         pointer.String(finalURL.String()),
+						Description: link.Description,
+						IconClass:   link.IconClass,
+					})
+				}
+			default:
+				errors = append(errors, fmt.Sprintf("link condition '%v' evaluated to non-boolean value for resource %v", *link.Condition, obj.GetName()))
+				continue
+			}
+		} else {
+			finalLinks = append(finalLinks, &application.LinkInfo{
+				Title:       pointer.String(link.Title),
+				Url:         pointer.String(finalURL.String()),
+				Description: link.Description,
+				IconClass:   link.IconClass,
+			})
+		}
+	}
+	return &application.LinksResponse{
+		Items: finalLinks,
+	}, errors
+}
diff --git a/server/project/project.go b/server/project/project.go
index d077b3bbc..9c86cf1ea 100644
--- a/server/project/project.go
+++ b/server/project/project.go
@@ -6,8 +6,7 @@ import (
 	"reflect"
 	"strings"
 
-	"github.com/argoproj/argo-cd/v2/util/db"
-
+	"github.com/argoproj/gitops-engine/pkg/utils/kube"
 	"github.com/argoproj/pkg/sync"
 	"github.com/golang-jwt/jwt/v4"
 	"github.com/google/uuid"
@@ -22,12 +21,15 @@ import (
 	"k8s.io/client-go/tools/cache"
 	"k8s.io/client-go/util/retry"
 
+	"github.com/argoproj/argo-cd/v2/pkg/apiclient/application"
 	"github.com/argoproj/argo-cd/v2/pkg/apiclient/project"
 	"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
 	appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
 	listersv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
+	"github.com/argoproj/argo-cd/v2/server/deeplinks"
 	"github.com/argoproj/argo-cd/v2/server/rbacpolicy"
 	"github.com/argoproj/argo-cd/v2/util/argo"
+	"github.com/argoproj/argo-cd/v2/util/db"
 	jwtutil "github.com/argoproj/argo-cd/v2/util/jwt"
 	"github.com/argoproj/argo-cd/v2/util/rbac"
 	"github.com/argoproj/argo-cd/v2/util/session"
@@ -155,6 +157,39 @@ func (s *Server) createToken(ctx context.Context, q *project.ProjectTokenCreateR
 
 }
 
+func (s *Server) ListLinks(ctx context.Context, q *project.ListProjectLinksRequest) (*application.LinksResponse, error) {
+	projName := q.GetName()
+
+	if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceProjects, rbacpolicy.ActionGet, projName); err != nil {
+		log.WithFields(map[string]interface{}{
+			"project": projName,
+		}).Warnf("unauthorized access to project, error=%v", err.Error())
+		return nil, fmt.Errorf("unauthorized access to project %v", projName)
+	}
+
+	proj, err := s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).Get(ctx, projName, metav1.GetOptions{})
+	if err != nil {
+		return nil, err
+	}
+
+	obj, err := kube.ToUnstructured(proj)
+	if err != nil {
+		return nil, fmt.Errorf("error getting application: %w", err)
+	}
+
+	deepLinks, err := s.settingsMgr.GetDeepLinks(settings.ProjectDeepLinks)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read application deep links from configmap: %w", err)
+	}
+
+	finalList, errorList := deeplinks.EvaluateDeepLinksResponse(*obj, deepLinks)
+	if len(errorList) > 0 {
+		log.Errorf("errorList while evaluating project deep links, %v", strings.Join(errorList, ", "))
+	}
+
+	return finalList, nil
+}
+
 // DeleteToken deletes a token in a project
 func (s *Server) DeleteToken(ctx context.Context, q *project.ProjectTokenDeleteRequest) (*project.EmptyResponse, error) {
 	var resp *project.EmptyResponse
diff --git a/server/project/project.proto b/server/project/project.proto
index 7d175000f..5cf4bc977 100644
--- a/server/project/project.proto
+++ b/server/project/project.proto
@@ -9,7 +9,7 @@ package project;
 import "google/api/annotations.proto";
 import "k8s.io/api/core/v1/generated.proto";
 import "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1/generated.proto";
-
+import "github.com/argoproj/argo-cd/v2/server/application/application.proto";
 
 // ProjectCreateRequest defines project creation parameters.
 message ProjectCreateRequest {
@@ -70,6 +70,10 @@ message DetailedProjectsResponse {
     repeated github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Cluster clusters = 4;
 }
 
+message ListProjectLinksRequest {
+  string name = 1;
+}
+
 // ProjectService
 service ProjectService {
 
@@ -136,4 +140,10 @@ service ProjectService {
   rpc GetSyncWindowsState(SyncWindowsQuery) returns (SyncWindowsResponse) {
       option (google.api.http).get = "/api/v1/projects/{name}/syncwindows";
   }
+
+  // ListLinks returns all deep links for the particular project
+  rpc ListLinks(ListProjectLinksRequest) returns (application.LinksResponse) {
+    option (google.api.http).get = "/api/v1/projects/{name}/links";
+  }
+
 }
\ No newline at end of file
diff --git a/ui/src/app/applications/components/application-deployment-history/application-deployment-history.tsx b/ui/src/app/applications/components/application-deployment-history/application-deployment-history.tsx
index 73542ca5b..b612c9ed9 100644
--- a/ui/src/app/applications/components/application-deployment-history/application-deployment-history.tsx
+++ b/ui/src/app/applications/components/application-deployment-history/application-deployment-history.tsx
@@ -32,13 +32,13 @@ export const ApplicationDeploymentHistory = ({
                 <div className='row application-deployment-history__item' key={info.deployedAt} onClick={() => selectDeployment(index)}>
                     <div className='columns small-3'>
                         <div>
-                            <i className='fa fa-clock' /> Deployed At:
+                            <i className='fa fa-clock' /> <span class='show-for-large'>Deployed At:</span>
                             <br />
                             <Timestamp date={info.deployedAt} />
                         </div>
                         <div>
                             <br />
-                            <i className='fa fa-hourglass-half' /> Time to deploy:
+                            <i className='fa fa-hourglass-half' /> <span class='show-for-large'>Time to deploy:</span>
                             <br />
                             {(info.deployStartedAt && <Duration durationMs={moment(info.deployedAt).diff(moment(info.deployStartedAt)) / 1000} />) || 'Unknown'}
                         </div>
diff --git a/ui/src/app/applications/components/application-details/application-details.scss b/ui/src/app/applications/components/application-details/application-details.scss
index 4ea04076f..9f7433d2f 100644
--- a/ui/src/app/applications/components/application-details/application-details.scss
+++ b/ui/src/app/applications/components/application-details/application-details.scss
@@ -274,3 +274,74 @@ $header: 120px;
         }
     }
 }
+
+
+@media screen and (max-width: map-get($breakpoints, large)) {
+.sliding-panel__body {
+    padding: 4px !important;
+}
+.sliding-panel--is-middle .sliding-panel__wrapper {
+    width: 90% !important;
+}
+.sliding-panel--is-middle .sliding-panel__body {
+    padding: 18px !important;
+}
+.sliding-panel__close {
+    z-index: 2 !important;
+}
+.top-bar__title {
+    display: none;
+}
+
+.top-bar__left-side {
+    white-space: normal !important;
+}
+.top-bar__left-side > div {
+    display: block !important;
+}
+.top-bar__right-side {
+    justify-content: right !important;
+}
+.application-status-panel.row {
+    flex-flow: unset;
+}
+.application-status-panel__item label {
+    margin-right: 0;
+}
+.application-status-panel__item {
+    padding: 5px 10px;
+}
+
+.white-box, .tabs__content {
+    padding: 4px !important;
+}
+.white-box__details-row .columns.small-3 {
+    overflow-wrap: unset !important;
+    overflow: scroll;
+}
+.white-box__details-row .columns.small-9{
+    padding-left: 4px;
+}
+
+.resource-details__header h1 {
+    font-size: 16px;
+}
+.resource-details__header {
+    margin-top: 30px;
+    padding-right: 4px;
+}
+
+.tabs__nav a:first-child, .tabs__nav a {
+    margin-left: 0 !important;
+}
+
+.editable-panel__buttons {
+    top: unset;
+}
+}
+
+@media screen and (max-width: map-get($breakpoints, medium)) {
+.sb-page-wrapper .top-bar.row {
+    display: none !important;
+}
+}
\ No newline at end of file
diff --git a/ui/src/app/applications/components/application-status-panel/application-status-panel.tsx b/ui/src/app/applications/components/application-status-panel/application-status-panel.tsx
index e8422c0f3..9bbb23370 100644
--- a/ui/src/app/applications/components/application-status-panel/application-status-panel.tsx
+++ b/ui/src/app/applications/components/application-status-panel/application-status-panel.tsx
@@ -88,7 +88,7 @@ export const ApplicationStatusPanel = ({application, showOperation, showConditio
                         <div>
                             <ComparisonStatusIcon status={application.status.sync.status} label={true} />
                         </div>
-                        <div className='application-status-panel__item-value__revision'>{syncStatusMessage(application)}</div>
+                        <div className='application-status-panel__item-value__revision show-for-large'>{syncStatusMessage(application)}</div>
                     </div>
                     {application.status && application.status.sync && application.status.sync.revision && !application.spec.source.chart && (
                         <div className='application-status-panel__item-name'>
@@ -121,7 +121,7 @@ export const ApplicationStatusPanel = ({application, showOperation, showConditio
                                 <OperationState app={application} />{' '}
                             </a>
                             {appOperationState.syncResult && appOperationState.syncResult.revision && (
-                                <div className='application-status-panel__item-value__revision'>
+                                <div className='application-status-panel__item-value__revision show-for-large'>
                                     To <Revision repoUrl={application.spec.source.repoURL} revision={appOperationState.syncResult.revision} />
                                 </div>
                             )}
diff --git a/ui/src/app/applications/components/application-summary/application-summary.tsx b/ui/src/app/applications/components/application-summary/application-summary.tsx
index 24f0bd9c3..4323f5df3 100644
--- a/ui/src/app/applications/components/application-summary/application-summary.tsx
+++ b/ui/src/app/applications/components/application-summary/application-summary.tsx
@@ -174,7 +174,7 @@ export const ApplicationSummary = (props: ApplicationSummaryProps) => {
                               load={src => services.repos.charts(src.repoURL).catch(() => new Array<models.HelmChart>())}>
                               {(charts: models.HelmChart[]) => (
                                   <div className='row'>
-                                      <div className='columns small-10'>
+                                      <div className='columns small-8'>
                                           <FormField
                                               formApi={formApi}
                                               field='spec.source.chart'
@@ -192,7 +192,7 @@ export const ApplicationSummary = (props: ApplicationSummaryProps) => {
                                               return (chartInfo && chartInfo.versions) || new Array<string>();
                                           }}>
                                           {(versions: string[]) => (
-                                              <div className='columns small-2'>
+                                              <div className='columns small-4'>
                                                   <FormField
                                                       formApi={formApi}
                                                       field='spec.source.targetRevision'
diff --git a/ui/src/app/applications/components/applications-list/applications-list.scss b/ui/src/app/applications/components/applications-list/applications-list.scss
index 5325f6387..fbe03245e 100644
--- a/ui/src/app/applications/components/applications-list/applications-list.scss
+++ b/ui/src/app/applications/components/applications-list/applications-list.scss
@@ -4,6 +4,9 @@
 
 .applications-list {
     padding: 1em;
+    @media screen and (max-width: 1024px) {
+        padding: 0;
+    }
     min-height: 88vh;
     &__title {
         font-weight: bolder;
@@ -208,3 +211,11 @@
 i.menu_icon {
     vertical-align: middle;
 }
+
+.argo-button {
+    i {
+        @media screen and (max-width: map-get($breakpoints, large)) {
+            margin: 0 auto !important;
+        }
+    }
+}
\ No newline at end of file
diff --git a/ui/src/app/applications/components/applications-list/applications-list.tsx b/ui/src/app/applications/components/applications-list/applications-list.tsx
index eff091e4c..9b5e66417 100644
--- a/ui/src/app/applications/components/applications-list/applications-list.tsx
+++ b/ui/src/app/applications/components/applications-list/applications-list.tsx
@@ -291,7 +291,7 @@ const FlexTopBar = (props: {toolbar: Toolbar | Observable<Toolbar>}) => {
                                                 style={{marginRight: 2}}
                                                 key={i}>
                                                 {item.iconClassName && <i className={item.iconClassName} style={{marginLeft: '-5px', marginRight: '5px'}} />}
-                                                {item.title}
+                                                <span className='show-for-large'>{item.title}</span>
                                             </button>
                                         ))}
                                     </React.Fragment>
diff --git a/ui/src/app/applications/components/resource-details/resource-details.tsx b/ui/src/app/applications/components/resource-details/resource-details.tsx
index 14ab321ff..7b677a8d3 100644
--- a/ui/src/app/applications/components/resource-details/resource-details.tsx
+++ b/ui/src/app/applications/components/resource-details/resource-details.tsx
@@ -307,13 +307,13 @@ export const ResourceDetails = (props: ResourceDetailsProps) => {
                                     onClick={() => appContext.navigation.goto('.', {deploy: AppUtils.nodeKey(selectedNode)}, {replace: true})}
                                     style={{marginLeft: 'auto', marginRight: '5px'}}
                                     className='argo-button argo-button--base'>
-                                    <i className='fa fa-sync-alt' /> SYNC
+                                    <i className='fa fa-sync-alt' /> <span className='show-for-large'>SYNC</span>
                                 </button>
                                 <button
                                     onClick={() => AppUtils.deletePopup(appContext, selectedNode, application)}
                                     style={{marginRight: '5px'}}
                                     className='argo-button argo-button--base'>
-                                    <i className='fa fa-trash' /> DELETE
+                                    <i className='fa fa-trash' /> <span className='show-for-large'>DELETE</span>
                                 </button>
                                 <DropDown
                                     isMenu={true}
diff --git a/ui/src/app/shared/components/timestamp.tsx b/ui/src/app/shared/components/timestamp.tsx
index 05cd45238..034d21c24 100644
--- a/ui/src/app/shared/components/timestamp.tsx
+++ b/ui/src/app/shared/components/timestamp.tsx
@@ -4,7 +4,11 @@ import Moment from 'react-moment';
 export const Timestamp = ({date}: {date: string | number}) => {
     return (
         <span>
-            <Moment fromNow={true}>{date}</Moment> (<Moment local={true}>{date}</Moment>)
+            <Moment fromNow={true}>{date}</Moment>
+            <span className='show-for-large'>
+                {' '}
+                (<Moment local={true}>{date}</Moment>)
+            </span>
         </span>
     );
 };
diff --git a/util/helm/cmd.go b/util/helm/cmd.go
index 1f6c68efc..db98c0fce 100644
--- a/util/helm/cmd.go
+++ b/util/helm/cmd.go
@@ -89,26 +89,6 @@ func (c *Cmd) RegistryLogin(repo string, creds Creds) (string, error) {
 		args = append(args, "--password", creds.Password)
 	}
 
-	if creds.CAPath != "" {
-		args = append(args, "--ca-file", creds.CAPath)
-	}
-	if len(creds.CertData) > 0 {
-		filePath, closer, err := writeToTmp(creds.CertData)
-		if err != nil {
-			return "", err
-		}
-		defer argoio.Close(closer)
-		args = append(args, "--cert-file", filePath)
-	}
-	if len(creds.KeyData) > 0 {
-		filePath, closer, err := writeToTmp(creds.KeyData)
-		if err != nil {
-			return "", err
-		}
-		defer argoio.Close(closer)
-		args = append(args, "--key-file", filePath)
-	}
-
 	if creds.InsecureSkipVerify {
 		args = append(args, "--insecure")
 	}
@@ -119,26 +99,6 @@ func (c *Cmd) RegistryLogout(repo string, creds Creds) (string, error) {
 	args := []string{"registry", "logout"}
 	args = append(args, repo)
 
-	if creds.CAPath != "" {
-		args = append(args, "--ca-file", creds.CAPath)
-	}
-	if len(creds.CertData) > 0 {
-		filePath, closer, err := writeToTmp(creds.CertData)
-		if err != nil {
-			return "", err
-		}
-		defer argoio.Close(closer)
-		args = append(args, "--cert-file", filePath)
-	}
-	if len(creds.KeyData) > 0 {
-		filePath, closer, err := writeToTmp(creds.KeyData)
-		if err != nil {
-			return "", err
-		}
-		defer argoio.Close(closer)
-		args = append(args, "--key-file", filePath)
-	}
-
 	return c.run(args...)
 }
 
diff --git a/util/settings/settings.go b/util/settings/settings.go
index 80a1c6da4..ac668a99b 100644
--- a/util/settings/settings.go
+++ b/util/settings/settings.go
@@ -318,6 +318,20 @@ type RepositoryCredentials struct {
 	Type string `json:"type,omitempty"`
 }
 
+// DeepLink structure
+type DeepLink struct {
+	// URL that the deep link will redirect to
+	URL string `json:"url"`
+	// Title that will be displayed in the UI corresponding to that link
+	Title string `json:"title"`
+	// Description (optional) a description for what the deep link is about
+	Description *string `json:"description,omitempty"`
+	// IconClass (optional) a font-awesome icon class to be used when displaying the links in dropdown menus.
+	IconClass *string `json:"icon.class,omitempty"`
+	// Condition (optional) a conditional statement depending on which the deep link shall be rendered
+	Condition *string `json:"if,omitempty"`
+}
+
 const (
 	// settingServerSignatureKey designates the key for a server secret key inside a Kubernetes secret.
 	settingServerSignatureKey = "server.secretkey"
@@ -423,6 +437,12 @@ const (
 	execShellsKey = "exec.shells"
 	// oidcTLSInsecureSkipVerifyKey is the key to configure whether TLS cert verification is skipped for OIDC connections
 	oidcTLSInsecureSkipVerifyKey = "oidc.tls.insecure.skip.verify"
+	// ApplicationDeepLinks is the application deep link key
+	ApplicationDeepLinks = "application.links"
+	// ProjectDeepLinks is the project deep link key
+	ProjectDeepLinks = "project.links"
+	// ResourceDeepLinks is the resource deep link key
+	ResourceDeepLinks = "resource.links"
 )
 
 var (
@@ -701,6 +721,21 @@ func (mgr *SettingsManager) GetConfigManagementPlugins() ([]v1alpha1.ConfigManag
 	return plugins, nil
 }
 
+func (mgr *SettingsManager) GetDeepLinks(deeplinkType string) ([]DeepLink, error) {
+	argoCDCM, err := mgr.getConfigMap()
+	if err != nil {
+		return nil, err
+	}
+	deepLinks := make([]DeepLink, 0)
+	if value, ok := argoCDCM.Data[deeplinkType]; ok {
+		err := yaml.Unmarshal([]byte(value), &deepLinks)
+		if err != nil {
+			return nil, err
+		}
+	}
+	return deepLinks, nil
+}
+
 func (mgr *SettingsManager) GetEnabledSourceTypes() (map[string]bool, error) {
 	argoCDCM, err := mgr.getConfigMap()
 	if err != nil {


Test patch:
diff --git a/reposerver/repository/repository_test.go b/reposerver/repository/repository_test.go
index d8cbab3cd..ef1d4a2ed 100644
--- a/reposerver/repository/repository_test.go
+++ b/reposerver/repository/repository_test.go
@@ -47,14 +47,14 @@ gpg:                using RSA key 4AEE18F83AFDEB23
 gpg: Good signature from "GitHub (web-flow commit signing) <noreply@github.com>" [ultimate]
 `
 
-type clientFunc func(*gitmocks.Client)
+type clientFunc func(*gitmocks.Client, *helmmocks.Client)
 
 func newServiceWithMocks(root string, signed bool) (*Service, *gitmocks.Client) {
 	root, err := filepath.Abs(root)
 	if err != nil {
 		panic(err)
 	}
-	return newServiceWithOpt(func(gitClient *gitmocks.Client) {
+	return newServiceWithOpt(func(gitClient *gitmocks.Client, helmClient *helmmocks.Client) {
 		gitClient.On("Init").Return(nil)
 		gitClient.On("Fetch", mock.Anything).Return(nil)
 		gitClient.On("Checkout", mock.Anything, mock.Anything).Return(nil)
@@ -66,31 +66,32 @@ func newServiceWithMocks(root string, signed bool) (*Service, *gitmocks.Client)
 		} else {
 			gitClient.On("VerifyCommitSignature", mock.Anything).Return("", nil)
 		}
+
+		chart := "my-chart"
+		oobChart := "out-of-bounds-chart"
+		version := "1.1.0"
+		helmClient.On("GetIndex", true).Return(&helm.Index{Entries: map[string]helm.Entries{
+			chart:    {{Version: "1.0.0"}, {Version: version}},
+			oobChart: {{Version: "1.0.0"}, {Version: version}},
+		}}, nil)
+		helmClient.On("ExtractChart", chart, version).Return("./testdata/my-chart", io.NopCloser, nil)
+		helmClient.On("ExtractChart", oobChart, version).Return("./testdata2/out-of-bounds-chart", io.NopCloser, nil)
+		helmClient.On("CleanChartCache", chart, version).Return(nil)
+		helmClient.On("CleanChartCache", oobChart, version).Return(nil)
+		helmClient.On("DependencyBuild").Return(nil)
 	}, root)
 }
 
 func newServiceWithOpt(cf clientFunc, root string) (*Service, *gitmocks.Client) {
 	helmClient := &helmmocks.Client{}
 	gitClient := &gitmocks.Client{}
-	cf(gitClient)
+	cf(gitClient, helmClient)
 	service := NewService(metrics.NewMetricsServer(), cache.NewCache(
 		cacheutil.NewCache(cacheutil.NewInMemoryCache(1*time.Minute)),
 		1*time.Minute,
 		1*time.Minute,
 	), RepoServerInitConstants{ParallelismLimit: 1}, argo.NewResourceTracking(), &git.NoopCredsStore{}, root)
 
-	chart := "my-chart"
-	oobChart := "out-of-bounds-chart"
-	version := "1.1.0"
-	helmClient.On("GetIndex", true).Return(&helm.Index{Entries: map[string]helm.Entries{
-		chart:    {{Version: "1.0.0"}, {Version: version}},
-		oobChart: {{Version: "1.0.0"}, {Version: version}},
-	}}, nil)
-	helmClient.On("ExtractChart", chart, version).Return("./testdata/my-chart", io.NopCloser, nil)
-	helmClient.On("ExtractChart", oobChart, version).Return("./testdata2/out-of-bounds-chart", io.NopCloser, nil)
-	helmClient.On("CleanChartCache", chart, version).Return(nil)
-	helmClient.On("CleanChartCache", oobChart, version).Return(nil)
-
 	service.newGitClient = func(rawRepoURL string, root string, creds git.Creds, insecure bool, enableLfs bool, prosy string, opts ...git.ClientOpts) (client git.Client, e error) {
 		return gitClient, nil
 	}
@@ -121,7 +122,7 @@ func newServiceWithCommitSHA(root, revision string) *Service {
 		revisionErr = errors.New("not a commit SHA")
 	}
 
-	service, gitClient := newServiceWithOpt(func(gitClient *gitmocks.Client) {
+	service, gitClient := newServiceWithOpt(func(gitClient *gitmocks.Client, helmClient *helmmocks.Client) {
 		gitClient.On("Init").Return(nil)
 		gitClient.On("Fetch", mock.Anything).Return(nil)
 		gitClient.On("Checkout", mock.Anything, mock.Anything).Return(nil)
@@ -1190,6 +1191,7 @@ func TestListApps(t *testing.T) {
 		"kustomization_yml":                 "Kustomize",
 		"my-chart":                          "Helm",
 		"my-chart-2":                        "Helm",
+		"oci-dependencies":                  "Helm",
 		"out-of-bounds-values-file-link":    "Helm",
 		"values-files":                      "Helm",
 	}
@@ -2519,3 +2521,18 @@ func Test_populateHelmAppDetails_values_symlinks(t *testing.T) {
 		assert.Empty(t, res.Helm.Parameters)
 	})
 }
+
+func TestOCIDependencies(t *testing.T) {
+	src := argoappv1.ApplicationSource{Path: "."}
+	q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, HelmRepoCreds: []*argoappv1.RepoCreds{
+		{URL: "example.com", Username: "test", Password: "test", EnableOCI: true},
+	}}
+
+	err := populateRequestRepos("./testdata/oci-dependencies", &q)
+	assert.Nil(t, err)
+
+	assert.Equal(t, len(q.Repos), 1)
+	assert.Equal(t, q.Repos[0].Username, "test")
+	assert.Equal(t, q.Repos[0].EnableOCI, true)
+	assert.Equal(t, q.Repos[0].Repo, "example.com")
+}
diff --git a/reposerver/repository/testdata/oci-dependencies/Chart.yaml b/reposerver/repository/testdata/oci-dependencies/Chart.yaml
new file mode 100644
index 000000000..3b39781ed
--- /dev/null
+++ b/reposerver/repository/testdata/oci-dependencies/Chart.yaml
@@ -0,0 +1,6 @@
+name: my-chart
+version: 1.1.0
+dependencies:
+- name: my-dependency
+  repository: oci://example.com
+  version: '*'
\ No newline at end of file
diff --git a/server/deeplinks/deeplinks_test.go b/server/deeplinks/deeplinks_test.go
new file mode 100644
index 000000000..9fc703a7e
--- /dev/null
+++ b/server/deeplinks/deeplinks_test.go
@@ -0,0 +1,101 @@
+package deeplinks
+
+import (
+	"github.com/argoproj/argo-cd/v2/pkg/apiclient/application"
+	"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
+	"github.com/argoproj/argo-cd/v2/util/settings"
+	"github.com/argoproj/gitops-engine/pkg/utils/kube"
+	"github.com/stretchr/testify/assert"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/utils/pointer"
+	"reflect"
+	"strings"
+	"testing"
+)
+
+type deepLinkTC struct {
+	obj         *unstructured.Unstructured
+	inputLinks  []settings.DeepLink
+	outputLinks []*application.LinkInfo
+	error       []string
+}
+
+func TestDeepLinks(t *testing.T) {
+	appObj, err := kube.ToUnstructured(&v1alpha1.Application{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "test",
+			Namespace: "test",
+		},
+		Spec: v1alpha1.ApplicationSpec{
+			Destination: v1alpha1.ApplicationDestination{
+				Server:    "test.com",
+				Namespace: "testns",
+			},
+		},
+	})
+	assert.NoError(t, err)
+	testTable := []deepLinkTC{
+		{
+			obj: appObj,
+			inputLinks: []settings.DeepLink{{
+				Title: "link",
+				URL:   "http://example.com/{{ .metadata.name }}&{{ .spec.destination.namespace }}",
+			}},
+			outputLinks: []*application.LinkInfo{{
+				Title: pointer.String("link"),
+				Url:   pointer.String("http://example.com/test&testns"),
+			}},
+			error: []string{},
+		},
+		{
+			obj: appObj,
+			inputLinks: []settings.DeepLink{
+				{
+					Title:     "link",
+					URL:       "http://example.com/{{ .metadata.name }}&{{ .spec.destination.namespace }}",
+					Condition: pointer.String(`metadata.name matches "test"`),
+				},
+				{
+					Title:     "link1",
+					URL:       "http://example.com/{{ .metadata.name }}&{{ .spec.destination.namespace }}",
+					Condition: pointer.String(`metadata.name matches "test1"`),
+				},
+				{
+					Title:     "link2",
+					URL:       "http://example.com/{{ .metadata.name }}&{{ .spec.destination.namespace }}",
+					Condition: pointer.String(`metadata.test matches "test"`),
+				}},
+			outputLinks: []*application.LinkInfo{{
+				Title: pointer.String("link"),
+				Url:   pointer.String("http://example.com/test&testns"),
+			}},
+			error: []string{"failed to evaluate link condition 'metadata.test matches \"test\"' with resource test, error=interface conversion: interface {} is nil, not string (1:15)\n | metadata.test matches \"test\"\n | ..............^"},
+		},
+		{
+			obj: appObj,
+			inputLinks: []settings.DeepLink{
+				{
+					Title:     "link",
+					URL:       "http://example.com/{{ .metadata.name }}&{{ .spec.destination.namespace }}",
+					Condition: pointer.String(`metadata.name matches "test"`),
+				},
+				{
+					Title:     "link1",
+					URL:       "http://example.com/{{ .metadata.name }}&{{ .spec.destination.namespace }}",
+					Condition: pointer.String(`1 + 1`),
+				}},
+			outputLinks: []*application.LinkInfo{{
+				Title: pointer.String("link"),
+				Url:   pointer.String("http://example.com/test&testns"),
+			}},
+			error: []string{"link condition '1 + 1' evaluated to non-boolean value for resource test"},
+		},
+	}
+
+	for _, tc := range testTable {
+		output, err := EvaluateDeepLinksResponse(*tc.obj, tc.inputLinks)
+		assert.Equal(t, tc.error, err, strings.Join(err, ","))
+		assert.Equal(t, reflect.DeepEqual(output.Items, tc.outputLinks), true)
+	}
+}