diff --git a/app/Livewire/Server/Sentinel.php b/app/Livewire/Server/Sentinel.php
new file mode 100644
index 000000000..20135f3c9
--- /dev/null
+++ b/app/Livewire/Server/Sentinel.php
@@ -0,0 +1,182 @@
+server->team_id ?? auth()->user()->currentTeam()->id;
+
+ return [
+ "echo-private:team.{$teamId},SentinelRestarted" => 'handleSentinelRestarted',
+ ];
+ }
+
+ public function mount(string $server_uuid)
+ {
+ try {
+ $this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail();
+ $this->parameters = get_route_parameters();
+ $this->syncData();
+ } catch (\Throwable) {
+ return redirect()->route('server.index');
+ }
+ }
+
+ public function syncData(bool $toModel = false)
+ {
+ if ($toModel) {
+ $this->authorize('update', $this->server);
+ $this->validate();
+ $this->server->settings->is_metrics_enabled = $this->isMetricsEnabled;
+ $this->server->settings->sentinel_token = $this->sentinelToken;
+ $this->server->settings->sentinel_metrics_refresh_rate_seconds = $this->sentinelMetricsRefreshRateSeconds;
+ $this->server->settings->sentinel_metrics_history_days = $this->sentinelMetricsHistoryDays;
+ $this->server->settings->sentinel_push_interval_seconds = $this->sentinelPushIntervalSeconds;
+ $this->server->settings->sentinel_custom_url = $this->sentinelCustomUrl;
+ $this->server->settings->is_sentinel_enabled = $this->isSentinelEnabled;
+ $this->server->settings->is_sentinel_debug_enabled = $this->isSentinelDebugEnabled;
+ $this->server->settings->save();
+ } else {
+ $this->isMetricsEnabled = $this->server->settings->is_metrics_enabled;
+ $this->sentinelToken = $this->server->settings->sentinel_token;
+ $this->sentinelMetricsRefreshRateSeconds = $this->server->settings->sentinel_metrics_refresh_rate_seconds;
+ $this->sentinelMetricsHistoryDays = $this->server->settings->sentinel_metrics_history_days;
+ $this->sentinelPushIntervalSeconds = $this->server->settings->sentinel_push_interval_seconds;
+ $this->sentinelCustomUrl = $this->server->settings->sentinel_custom_url;
+ $this->isSentinelEnabled = $this->server->settings->is_sentinel_enabled;
+ $this->isSentinelDebugEnabled = $this->server->settings->is_sentinel_debug_enabled;
+ $this->sentinelUpdatedAt = $this->server->sentinel_updated_at;
+ }
+ }
+
+ public function handleSentinelRestarted($event)
+ {
+ if ($event['serverUuid'] === $this->server->uuid) {
+ $this->server->refresh();
+ $this->syncData();
+ $this->dispatch('success', 'Sentinel has been restarted successfully.');
+ }
+ }
+
+ public function restartSentinel()
+ {
+ try {
+ $this->authorize('manageSentinel', $this->server);
+ $customImage = isDev() ? $this->sentinelCustomDockerImage : null;
+ $this->server->restartSentinel($customImage);
+ $this->dispatch('info', 'Restarting Sentinel.');
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
+ }
+ }
+
+ public function updatedIsSentinelDebugEnabled($value)
+ {
+ try {
+ $this->submit();
+ $this->restartSentinel();
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
+ }
+ }
+
+ public function updatedIsMetricsEnabled($value)
+ {
+ try {
+ $this->submit();
+ $this->restartSentinel();
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
+ }
+ }
+
+ public function updatedIsSentinelEnabled($value)
+ {
+ try {
+ $this->authorize('manageSentinel', $this->server);
+ if ($value === true) {
+ if ($this->server->isBuildServer()) {
+ $this->isSentinelEnabled = false;
+ $this->dispatch('error', 'Sentinel cannot be enabled on build servers.');
+
+ return;
+ }
+ $customImage = isDev() ? $this->sentinelCustomDockerImage : null;
+ StartSentinel::run($this->server, true, null, $customImage);
+ } else {
+ $this->isMetricsEnabled = false;
+ $this->isSentinelDebugEnabled = false;
+ StopSentinel::dispatch($this->server);
+ }
+ $this->submit();
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
+ }
+ }
+
+ public function regenerateSentinelToken()
+ {
+ try {
+ $this->authorize('manageSentinel', $this->server);
+ $this->server->settings->generateSentinelToken();
+ $this->dispatch('success', 'Token regenerated. Restarting Sentinel.');
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
+ }
+ }
+
+ public function submit()
+ {
+ try {
+ $this->syncData(true);
+ $this->dispatch('success', 'Sentinel settings updated.');
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
+ }
+ }
+
+ public function render()
+ {
+ return view('livewire.server.sentinel');
+ }
+}
diff --git a/app/Livewire/Server/Show.php b/app/Livewire/Server/Show.php
index 7a4a1c480..cc55da491 100644
--- a/app/Livewire/Server/Show.php
+++ b/app/Livewire/Server/Show.php
@@ -81,6 +81,8 @@ class Show extends Component
public ?int $selectedHetznerTokenId = null;
+ public ?string $manualHetznerServerId = null;
+
public ?array $matchedHetznerServer = null;
public ?string $hetznerSearchError = null;
@@ -447,6 +449,10 @@ public function handleServerValidated($event = null)
// Update validation state
$this->isValidating = $this->server->is_validating ?? false;
+
+ // Reload Hetzner tokens in case the linking section should now be shown
+ $this->loadHetznerTokens();
+
$this->dispatch('refreshServerShow');
$this->dispatch('refreshServer');
}
@@ -524,6 +530,47 @@ public function searchHetznerServer(): void
}
}
+ public function searchHetznerServerById(): void
+ {
+ $this->hetznerSearchError = null;
+ $this->hetznerNoMatchFound = false;
+ $this->matchedHetznerServer = null;
+
+ if (! $this->selectedHetznerTokenId) {
+ $this->hetznerSearchError = 'Please select a Hetzner token first.';
+
+ return;
+ }
+
+ if (! $this->manualHetznerServerId) {
+ $this->hetznerSearchError = 'Please enter a Hetzner Server ID.';
+
+ return;
+ }
+
+ try {
+ $this->authorize('update', $this->server);
+
+ $token = $this->availableHetznerTokens->firstWhere('id', $this->selectedHetznerTokenId);
+ if (! $token) {
+ $this->hetznerSearchError = 'Invalid token selected.';
+
+ return;
+ }
+
+ $hetznerService = new HetznerService($token->token);
+ $serverData = $hetznerService->getServer((int) $this->manualHetznerServerId);
+
+ if (! empty($serverData)) {
+ $this->matchedHetznerServer = $serverData;
+ } else {
+ $this->hetznerNoMatchFound = true;
+ }
+ } catch (\Throwable $e) {
+ $this->hetznerSearchError = 'Failed to fetch Hetzner server: '.$e->getMessage();
+ }
+ }
+
public function linkToHetzner()
{
if (! $this->matchedHetznerServer) {
@@ -564,6 +611,7 @@ public function linkToHetzner()
// Clear the linking state
$this->matchedHetznerServer = null;
$this->selectedHetznerTokenId = null;
+ $this->manualHetznerServerId = null;
$this->hetznerNoMatchFound = false;
$this->hetznerSearchError = null;
diff --git a/app/Livewire/Server/Swarm.php b/app/Livewire/Server/Swarm.php
new file mode 100644
index 000000000..e3e441ea0
--- /dev/null
+++ b/app/Livewire/Server/Swarm.php
@@ -0,0 +1,59 @@
+server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail();
+ $this->parameters = get_route_parameters();
+ $this->syncData();
+ } catch (\Throwable) {
+ return redirect()->route('server.index');
+ }
+ }
+
+ public function syncData(bool $toModel = false)
+ {
+ if ($toModel) {
+ $this->authorize('update', $this->server);
+ $this->server->settings->is_swarm_manager = $this->isSwarmManager;
+ $this->server->settings->is_swarm_worker = $this->isSwarmWorker;
+ $this->server->settings->save();
+ } else {
+ $this->isSwarmManager = $this->server->settings->is_swarm_manager;
+ $this->isSwarmWorker = $this->server->settings->is_swarm_worker;
+ }
+ }
+
+ public function instantSave()
+ {
+ try {
+ $this->syncData(true);
+ $this->dispatch('success', 'Swarm settings updated.');
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
+ }
+ }
+
+ public function render()
+ {
+ return view('livewire.server.swarm');
+ }
+}
diff --git a/resources/views/components/server/sidebar.blade.php b/resources/views/components/server/sidebar.blade.php
index 2697b4c0b..3f5bcafac 100644
--- a/resources/views/components/server/sidebar.blade.php
+++ b/resources/views/components/server/sidebar.blade.php
@@ -6,6 +6,16 @@
href="{{ route('server.advanced', ['server_uuid' => $server->uuid]) }}">Advanced
@endif
+ @if (!$server->isBuildServer() && !$server->settings->is_cloudflare_tunnel)
+
+ @endif
+ @if ($server->isFunctional() && !$server->isSwarm() && !$server->isBuildServer())
+
+ @endif
diff --git a/resources/views/livewire/server/sentinel.blade.php b/resources/views/livewire/server/sentinel.blade.php
new file mode 100644
index 000000000..4016a30e4
--- /dev/null
+++ b/resources/views/livewire/server/sentinel.blade.php
@@ -0,0 +1,110 @@
+
+
+ {{ data_get_str($server, 'name')->limit(10) }} > Sentinel | Coolify
+
+
+
+
diff --git a/resources/views/livewire/server/show.blade.php b/resources/views/livewire/server/show.blade.php
index a8344df05..f58dc058b 100644
--- a/resources/views/livewire/server/show.blade.php
+++ b/resources/views/livewire/server/show.blade.php
@@ -285,37 +285,6 @@ class="w-full input opacity-50 cursor-not-allowed"
@endif
- @if (!$server->isBuildServer() && !$server->settings->is_cloudflare_tunnel)
- Swarm (experimental)
-
-
-
- @if ($server->settings->is_swarm_worker)
-
- @else
-
- @endif
-
- @if ($server->settings->is_swarm_manager)
-
- @else
-
- @endif
-
- @endif
@endif
@@ -337,6 +306,20 @@ class="w-full input opacity-50 cursor-not-allowed"
@endforeach
+
+
+
+
+ Search by ID
+ Searching...
+
+ OR
@@ -354,10 +337,14 @@ class="w-full input opacity-50 cursor-not-allowed"
@if ($hetznerNoMatchFound)
- No Hetzner server found matching IP: {{ $server->ip }}
+ @if ($manualHetznerServerId)
+ No Hetzner server found with ID: {{ $manualHetznerServerId }}
+ @else
+ No Hetzner server found matching IP: {{ $server->ip }}
+ @endif
- Try a different token or verify the server IP is correct.
+ Try a different token, enter the Server ID manually, or verify the details are correct.
@endif
@@ -378,116 +365,6 @@ class="w-full input opacity-50 cursor-not-allowed"
@endif
@endif
- @if ($server->isFunctional() && !$server->isSwarm() && !$server->isBuildServer())
-
- @endif
diff --git a/resources/views/livewire/server/swarm.blade.php b/resources/views/livewire/server/swarm.blade.php
new file mode 100644
index 000000000..1d18e2d31
--- /dev/null
+++ b/resources/views/livewire/server/swarm.blade.php
@@ -0,0 +1,43 @@
+
+
+ {{ data_get_str($server, 'name')->limit(10) }} > Swarm | Coolify
+
+
+
+
+
+
+
+
Swarm (experimental)
+
+
+
+
+
+ @if ($server->settings->is_swarm_worker)
+
+ @else
+
+ @endif
+
+ @if ($server->settings->is_swarm_manager)
+
+ @else
+
+ @endif
+
+
+
+
diff --git a/routes/web.php b/routes/web.php
index 703f80ab5..2a9072299 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -56,7 +56,9 @@
use App\Livewire\Server\Resources as ResourcesShow;
use App\Livewire\Server\Security\Patches;
use App\Livewire\Server\Security\TerminalAccess;
+use App\Livewire\Server\Sentinel as ServerSentinel;
use App\Livewire\Server\Show as ServerShow;
+use App\Livewire\Server\Swarm as ServerSwarm;
use App\Livewire\Settings\Advanced as SettingsAdvanced;
use App\Livewire\Settings\Index as SettingsIndex;
use App\Livewire\Settings\Updates as SettingsUpdates;
@@ -251,6 +253,8 @@
Route::prefix('server/{server_uuid}')->group(function () {
Route::get('/', ServerShow::class)->name('server.show');
Route::get('/advanced', ServerAdvanced::class)->name('server.advanced');
+ Route::get('/swarm', ServerSwarm::class)->name('server.swarm');
+ Route::get('/sentinel', ServerSentinel::class)->name('server.sentinel');
Route::get('/private-key', PrivateKeyShow::class)->name('server.private-key');
Route::get('/cloud-provider-token', CloudProviderTokenShow::class)->name('server.cloud-provider-token');
Route::get('/ca-certificate', CaCertificateShow::class)->name('server.ca-certificate');
diff --git a/templates/service-templates-latest.json b/templates/service-templates-latest.json
index 5c482630b..c3e33b582 100644
--- a/templates/service-templates-latest.json
+++ b/templates/service-templates-latest.json
@@ -4350,7 +4350,7 @@
"umami": {
"documentation": "https://umami.is?utm_source=coolify.io",
"slogan": "Umami is web analytics platform which provides insights into visitor behavior without compromising user privacy.",
- "compose": "c2VydmljZXM6CiAgdW1hbWk6CiAgICBpbWFnZTogJ2doY3IuaW8vdW1hbWktc29mdHdhcmUvdW1hbWk6My4wLjInCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX1VSTF9VTUFNSV8zMDAwCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3JlczovLyRTRVJWSUNFX1VTRVJfUE9TVEdSRVM6JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNAcG9zdGdyZXNxbDo1NDMyLyRQT1NUR1JFU19EQicKICAgICAgLSBEQVRBQkFTRV9UWVBFPXBvc3RncmVzCiAgICAgIC0gQVBQX1NFQ1JFVD0kU0VSVklDRV9QQVNTV09SRF82NF9VTUFNSQogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXNxbDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjMwMDAvYXBpL2hlYXJ0YmVhdCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIHBvc3RncmVzcWw6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3Bvc3RncmVzcWwtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQT1NUR1JFU19VU0VSPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi11bWFtaX0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==",
+ "compose": "c2VydmljZXM6CiAgdW1hbWk6CiAgICBpbWFnZTogJ2doY3IuaW8vdW1hbWktc29mdHdhcmUvdW1hbWk6My4wLjMnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX1VSTF9VTUFNSV8zMDAwCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3JlczovLyRTRVJWSUNFX1VTRVJfUE9TVEdSRVM6JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNAcG9zdGdyZXNxbDo1NDMyLyRQT1NUR1JFU19EQicKICAgICAgLSBEQVRBQkFTRV9UWVBFPXBvc3RncmVzCiAgICAgIC0gQVBQX1NFQ1JFVD0kU0VSVklDRV9QQVNTV09SRF82NF9VTUFNSQogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXNxbDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjMwMDAvYXBpL2hlYXJ0YmVhdCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIHBvc3RncmVzcWw6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3Bvc3RncmVzcWwtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQT1NUR1JFU19VU0VSPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi11bWFtaX0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==",
"tags": [
"analytics",
"insights",
diff --git a/templates/service-templates.json b/templates/service-templates.json
index 226657fad..aae653dac 100644
--- a/templates/service-templates.json
+++ b/templates/service-templates.json
@@ -4350,7 +4350,7 @@
"umami": {
"documentation": "https://umami.is?utm_source=coolify.io",
"slogan": "Umami is web analytics platform which provides insights into visitor behavior without compromising user privacy.",
- "compose": "c2VydmljZXM6CiAgdW1hbWk6CiAgICBpbWFnZTogJ2doY3IuaW8vdW1hbWktc29mdHdhcmUvdW1hbWk6My4wLjInCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fVU1BTUlfMzAwMAogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTQHBvc3RncmVzcWw6NTQzMi8kUE9TVEdSRVNfREInCiAgICAgIC0gREFUQUJBU0VfVFlQRT1wb3N0Z3JlcwogICAgICAtIEFQUF9TRUNSRVQ9JFNFUlZJQ0VfUEFTU1dPUkRfNjRfVU1BTUkKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzcWw6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwL2FwaS9oZWFydGJlYXQnCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICBwb3N0Z3Jlc3FsOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNi1hbHBpbmUnCiAgICB2b2x1bWVzOgogICAgICAtICdwb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotdW1hbWl9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=",
+ "compose": "c2VydmljZXM6CiAgdW1hbWk6CiAgICBpbWFnZTogJ2doY3IuaW8vdW1hbWktc29mdHdhcmUvdW1hbWk6My4wLjMnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fVU1BTUlfMzAwMAogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTQHBvc3RncmVzcWw6NTQzMi8kUE9TVEdSRVNfREInCiAgICAgIC0gREFUQUJBU0VfVFlQRT1wb3N0Z3JlcwogICAgICAtIEFQUF9TRUNSRVQ9JFNFUlZJQ0VfUEFTU1dPUkRfNjRfVU1BTUkKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzcWw6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwL2FwaS9oZWFydGJlYXQnCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICBwb3N0Z3Jlc3FsOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNi1hbHBpbmUnCiAgICB2b2x1bWVzOgogICAgICAtICdwb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotdW1hbWl9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=",
"tags": [
"analytics",
"insights",