From 8b7dbbafb25eb8ac4e572b6dd16a65bac1f524a0 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Thu, 30 Apr 2026 11:28:06 +0200 Subject: [PATCH 01/12] fix(api): hide sensitive fields by default, expose via makeVisible for privileged tokens Models now declare $hidden for passwords, tokens, db URLs, and compose fields. API controllers flip from makeHidden-on-deny to makeVisible-on- allow (can_read_sensitive=true), fixing fields that were never hidden. Also adds missing fields (mysql/mariadb passwords, logdrain keys, etc.) to privileged disclosure lists. Tests added: Feature/Security/ApiSensitiveFieldsTest and Unit/Models/SensitiveFieldsHiddenTest cover all affected models and controllers. --- .../Api/ApplicationsController.php | 11 +- .../Controllers/Api/DatabasesController.php | 13 +- .../Controllers/Api/ServersController.php | 16 +- .../Controllers/Api/ServicesController.php | 4 +- app/Models/Application.php | 18 ++ app/Models/EnvironmentVariable.php | 14 +- app/Models/Server.php | 10 + app/Models/ServerSetting.php | 14 ++ app/Models/Service.php | 11 ++ app/Models/StandaloneClickhouse.php | 11 ++ app/Models/StandaloneDragonfly.php | 11 ++ app/Models/StandaloneKeydb.php | 11 ++ app/Models/StandaloneMariadb.php | 12 ++ app/Models/StandaloneMongodb.php | 11 ++ app/Models/StandaloneMysql.php | 12 ++ app/Models/StandalonePostgresql.php | 12 ++ app/Models/StandaloneRedis.php | 11 ++ .../Security/ApiSensitiveFieldsTest.php | 184 ++++++++++++++++++ .../Unit/Models/SensitiveFieldsHiddenTest.php | 179 +++++++++++++++++ 19 files changed, 549 insertions(+), 16 deletions(-) create mode 100644 tests/Feature/Security/ApiSensitiveFieldsTest.php create mode 100644 tests/Unit/Models/SensitiveFieldsHiddenTest.php diff --git a/app/Http/Controllers/Api/ApplicationsController.php b/app/Http/Controllers/Api/ApplicationsController.php index bb72ebabe..9d9c5d33a 100644 --- a/app/Http/Controllers/Api/ApplicationsController.php +++ b/app/Http/Controllers/Api/ApplicationsController.php @@ -43,8 +43,8 @@ private function removeSensitiveData($application) 'resourceable_id', 'resourceable_type', ]); - if (request()->attributes->get('can_read_sensitive', false) === false) { - $application->makeHidden([ + if (request()->attributes->get('can_read_sensitive', false) === true) { + $application->makeVisible([ 'custom_labels', 'dockerfile', 'docker_compose', @@ -53,10 +53,13 @@ private function removeSensitiveData($application) 'manual_webhook_secret_gitea', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', - 'private_key_id', + 'http_basic_auth_password', 'value', 'real_value', - 'http_basic_auth_password', + ]); + } else { + $application->makeHidden([ + 'private_key_id', ]); } diff --git a/app/Http/Controllers/Api/DatabasesController.php b/app/Http/Controllers/Api/DatabasesController.php index dc9b6f5b5..acfc64355 100644 --- a/app/Http/Controllers/Api/DatabasesController.php +++ b/app/Http/Controllers/Api/DatabasesController.php @@ -33,16 +33,21 @@ private function removeSensitiveData($database) 'id', 'laravel_through_key', ]); - if (request()->attributes->get('can_read_sensitive', false) === false) { - $database->makeHidden([ + if (request()->attributes->get('can_read_sensitive', false) === true) { + $database->makeVisible([ 'internal_db_url', 'external_db_url', + 'init_scripts', 'postgres_password', 'dragonfly_password', 'redis_password', 'mongo_initdb_root_password', 'keydb_password', 'clickhouse_admin_password', + 'mysql_password', + 'mysql_root_password', + 'mariadb_password', + 'mariadb_root_password', ]); } @@ -2957,8 +2962,8 @@ private function removeSensitiveEnvData($env) 'resourceable_id', 'resourceable_type', ]); - if (request()->attributes->get('can_read_sensitive', false) === false) { - $env->makeHidden([ + if (request()->attributes->get('can_read_sensitive', false) === true) { + $env->makeVisible([ 'value', 'real_value', ]); diff --git a/app/Http/Controllers/Api/ServersController.php b/app/Http/Controllers/Api/ServersController.php index 6c3b2da00..cf50990dc 100644 --- a/app/Http/Controllers/Api/ServersController.php +++ b/app/Http/Controllers/Api/ServersController.php @@ -22,9 +22,14 @@ class ServersController extends Controller { private function removeSensitiveDataFromSettings($settings) { - if (request()->attributes->get('can_read_sensitive', false) === false) { - $settings = $settings->makeHidden([ + if (request()->attributes->get('can_read_sensitive', false) === true) { + $settings = $settings->makeVisible([ 'sentinel_token', + 'sentinel_custom_url', + 'logdrain_newrelic_license_key', + 'logdrain_axiom_api_key', + 'logdrain_custom_config', + 'logdrain_custom_config_parser', ]); } @@ -36,8 +41,11 @@ private function removeSensitiveData($server) $server->makeHidden([ 'id', ]); - if (request()->attributes->get('can_read_sensitive', false) === false) { - // Do nothing + if (request()->attributes->get('can_read_sensitive', false) === true) { + $server->makeVisible([ + 'logdrain_axiom_api_key', + 'logdrain_newrelic_license_key', + ]); } return serializeApiResponse($server); diff --git a/app/Http/Controllers/Api/ServicesController.php b/app/Http/Controllers/Api/ServicesController.php index 11a23d46c..4a04f499d 100644 --- a/app/Http/Controllers/Api/ServicesController.php +++ b/app/Http/Controllers/Api/ServicesController.php @@ -30,8 +30,8 @@ private function removeSensitiveData($service) 'resourceable_id', 'resourceable_type', ]); - if (request()->attributes->get('can_read_sensitive', false) === false) { - $service->makeHidden([ + if (request()->attributes->get('can_read_sensitive', false) === true) { + $service->makeVisible([ 'docker_compose_raw', 'docker_compose', 'value', diff --git a/app/Models/Application.php b/app/Models/Application.php index 85e94bfd6..f342112b9 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -215,6 +215,24 @@ class Application extends BaseModel protected $appends = ['server_status']; + /** + * Sensitive fields hidden by default in serialized output (toArray/toJson). + * API controllers should call makeVisible([...]) for callers with the + * `read:sensitive` or `root` token ability. Internal serializers (deployment + * job, compose generation) must makeVisible explicitly before toArray(). + */ + protected $hidden = [ + 'http_basic_auth_password', + 'manual_webhook_secret_github', + 'manual_webhook_secret_gitlab', + 'manual_webhook_secret_bitbucket', + 'manual_webhook_secret_gitea', + 'dockerfile', + 'docker_compose', + 'docker_compose_raw', + 'custom_labels', + ]; + protected function casts(): array { return [ diff --git a/app/Models/EnvironmentVariable.php b/app/Models/EnvironmentVariable.php index 83212267c..7dd9992a6 100644 --- a/app/Models/EnvironmentVariable.php +++ b/app/Models/EnvironmentVariable.php @@ -76,9 +76,19 @@ class EnvironmentVariable extends BaseModel protected $appends = ['real_value', 'is_shared', 'is_really_required', 'is_nixpacks', 'is_coolify']; + /** + * Sensitive fields hidden by default in serialized output (toArray/toJson). + * API controllers should call makeVisible([...]) for callers with the + * `read:sensitive` or `root` token ability. + */ + protected $hidden = [ + 'value', + 'real_value', + ]; + protected static function booted() { - static::created(function (EnvironmentVariable $environment_variable) { + static::created(function (ModelsEnvironmentVariable $environment_variable) { if ($environment_variable->resourceable_type === Application::class && ! $environment_variable->is_preview) { $found = ModelsEnvironmentVariable::where('key', $environment_variable->key) ->where('resourceable_type', Application::class) @@ -109,7 +119,7 @@ protected static function booted() ]); }); - static::saving(function (EnvironmentVariable $environmentVariable) { + static::saving(function (ModelsEnvironmentVariable $environmentVariable) { $environmentVariable->updateIsShared(); }); } diff --git a/app/Models/Server.php b/app/Models/Server.php index 74e8ba5b0..1c7eca844 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -254,6 +254,16 @@ public static function flushIdentityMap(): void 'force_disabled' => 'boolean', ]; + /** + * Sensitive fields hidden by default in serialized output (toArray/toJson). + * API controllers should call makeVisible([...]) for callers with the + * `read:sensitive` or `root` token ability. + */ + protected $hidden = [ + 'logdrain_axiom_api_key', + 'logdrain_newrelic_license_key', + ]; + protected $schemalessAttributes = [ 'proxy', ]; diff --git a/app/Models/ServerSetting.php b/app/Models/ServerSetting.php index 8d85c8932..807dc82ef 100644 --- a/app/Models/ServerSetting.php +++ b/app/Models/ServerSetting.php @@ -113,6 +113,20 @@ class ServerSetting extends Model 'connection_timeout' => 'integer', ]; + /** + * Sensitive fields hidden by default in serialized output (toArray/toJson). + * API controllers should call makeVisible([...]) for callers with the + * `read:sensitive` or `root` token ability. + */ + protected $hidden = [ + 'sentinel_token', + 'sentinel_custom_url', + 'logdrain_newrelic_license_key', + 'logdrain_axiom_api_key', + 'logdrain_custom_config', + 'logdrain_custom_config_parser', + ]; + protected static function booted() { static::creating(function ($setting) { diff --git a/app/Models/Service.php b/app/Models/Service.php index 11189b4ac..42b0dbede 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -67,6 +67,17 @@ class Service extends BaseModel protected $appends = ['server_status', 'status']; + /** + * Sensitive fields hidden by default in serialized output (toArray/toJson). + * API controllers should call makeVisible([...]) for callers with the + * `read:sensitive` or `root` token ability. Internal compose generators + * must makeVisible explicitly before toArray(). + */ + protected $hidden = [ + 'docker_compose', + 'docker_compose_raw', + ]; + protected static function booted() { static::creating(function ($service) { diff --git a/app/Models/StandaloneClickhouse.php b/app/Models/StandaloneClickhouse.php index 784e2c937..eb70c7070 100644 --- a/app/Models/StandaloneClickhouse.php +++ b/app/Models/StandaloneClickhouse.php @@ -48,6 +48,17 @@ class StandaloneClickhouse extends BaseModel protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status']; + /** + * Sensitive fields hidden by default in serialized output (toArray/toJson). + * API controllers should call makeVisible([...]) for callers with the + * `read:sensitive` or `root` token ability. + */ + protected $hidden = [ + 'clickhouse_admin_password', + 'internal_db_url', + 'external_db_url', + ]; + protected $casts = [ 'clickhouse_admin_password' => 'encrypted', 'public_port_timeout' => 'integer', diff --git a/app/Models/StandaloneDragonfly.php b/app/Models/StandaloneDragonfly.php index e07053c03..86250f8aa 100644 --- a/app/Models/StandaloneDragonfly.php +++ b/app/Models/StandaloneDragonfly.php @@ -47,6 +47,17 @@ class StandaloneDragonfly extends BaseModel protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status']; + /** + * Sensitive fields hidden by default in serialized output (toArray/toJson). + * API controllers should call makeVisible([...]) for callers with the + * `read:sensitive` or `root` token ability. + */ + protected $hidden = [ + 'dragonfly_password', + 'internal_db_url', + 'external_db_url', + ]; + protected $casts = [ 'dragonfly_password' => 'encrypted', 'public_port_timeout' => 'integer', diff --git a/app/Models/StandaloneKeydb.php b/app/Models/StandaloneKeydb.php index 979f45a3d..3b7990e09 100644 --- a/app/Models/StandaloneKeydb.php +++ b/app/Models/StandaloneKeydb.php @@ -48,6 +48,17 @@ class StandaloneKeydb extends BaseModel protected $appends = ['internal_db_url', 'external_db_url', 'server_status']; + /** + * Sensitive fields hidden by default in serialized output (toArray/toJson). + * API controllers should call makeVisible([...]) for callers with the + * `read:sensitive` or `root` token ability. + */ + protected $hidden = [ + 'keydb_password', + 'internal_db_url', + 'external_db_url', + ]; + protected $casts = [ 'keydb_password' => 'encrypted', 'public_port_timeout' => 'integer', diff --git a/app/Models/StandaloneMariadb.php b/app/Models/StandaloneMariadb.php index dba8a52f5..56d11cd94 100644 --- a/app/Models/StandaloneMariadb.php +++ b/app/Models/StandaloneMariadb.php @@ -51,6 +51,18 @@ class StandaloneMariadb extends BaseModel protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status']; + /** + * Sensitive fields hidden by default in serialized output (toArray/toJson). + * API controllers should call makeVisible([...]) for callers with the + * `read:sensitive` or `root` token ability. + */ + protected $hidden = [ + 'mariadb_password', + 'mariadb_root_password', + 'internal_db_url', + 'external_db_url', + ]; + protected $casts = [ 'mariadb_password' => 'encrypted', 'public_port_timeout' => 'integer', diff --git a/app/Models/StandaloneMongodb.php b/app/Models/StandaloneMongodb.php index e72f4f1c6..1acf09002 100644 --- a/app/Models/StandaloneMongodb.php +++ b/app/Models/StandaloneMongodb.php @@ -51,6 +51,17 @@ class StandaloneMongodb extends BaseModel protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status']; + /** + * Sensitive fields hidden by default in serialized output (toArray/toJson). + * API controllers should call makeVisible([...]) for callers with the + * `read:sensitive` or `root` token ability. + */ + protected $hidden = [ + 'mongo_initdb_root_password', + 'internal_db_url', + 'external_db_url', + ]; + protected $casts = [ 'public_port_timeout' => 'integer', 'restart_count' => 'integer', diff --git a/app/Models/StandaloneMysql.php b/app/Models/StandaloneMysql.php index 1c522d200..bebf60876 100644 --- a/app/Models/StandaloneMysql.php +++ b/app/Models/StandaloneMysql.php @@ -52,6 +52,18 @@ class StandaloneMysql extends BaseModel protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status']; + /** + * Sensitive fields hidden by default in serialized output (toArray/toJson). + * API controllers should call makeVisible([...]) for callers with the + * `read:sensitive` or `root` token ability. + */ + protected $hidden = [ + 'mysql_password', + 'mysql_root_password', + 'internal_db_url', + 'external_db_url', + ]; + protected $casts = [ 'mysql_password' => 'encrypted', 'mysql_root_password' => 'encrypted', diff --git a/app/Models/StandalonePostgresql.php b/app/Models/StandalonePostgresql.php index 57dfe5988..e76742d2c 100644 --- a/app/Models/StandalonePostgresql.php +++ b/app/Models/StandalonePostgresql.php @@ -54,6 +54,18 @@ class StandalonePostgresql extends BaseModel protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status']; + /** + * Sensitive fields hidden by default in serialized output (toArray/toJson). + * API controllers should call makeVisible([...]) for callers with the + * `read:sensitive` or `root` token ability. + */ + protected $hidden = [ + 'postgres_password', + 'init_scripts', + 'internal_db_url', + 'external_db_url', + ]; + protected $casts = [ 'init_scripts' => 'array', 'postgres_password' => 'encrypted', diff --git a/app/Models/StandaloneRedis.php b/app/Models/StandaloneRedis.php index ef42d7f18..08ba5fefe 100644 --- a/app/Models/StandaloneRedis.php +++ b/app/Models/StandaloneRedis.php @@ -47,6 +47,17 @@ class StandaloneRedis extends BaseModel protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status']; + /** + * Sensitive fields hidden by default in serialized output (toArray/toJson). + * API controllers should call makeVisible([...]) for callers with the + * `read:sensitive` or `root` token ability. + */ + protected $hidden = [ + 'redis_password', + 'internal_db_url', + 'external_db_url', + ]; + protected $casts = [ 'public_port_timeout' => 'integer', 'restart_count' => 'integer', diff --git a/tests/Feature/Security/ApiSensitiveFieldsTest.php b/tests/Feature/Security/ApiSensitiveFieldsTest.php new file mode 100644 index 000000000..b71881e3b --- /dev/null +++ b/tests/Feature/Security/ApiSensitiveFieldsTest.php @@ -0,0 +1,184 @@ + $team]); + $token = $user->createToken('sensitive-test', $abilities); + DB::table('personal_access_tokens')->where('id', $token->accessToken->id)->update([ + 'team_id' => $team->id, + ]); + + return $token->plainTextToken; +} + +function makeTeamUser(): array +{ + $team = Team::factory()->create(); + $user = User::factory()->create(); + $team->members()->attach($user->id, ['role' => 'owner']); + session(['currentTeam' => $team]); + + return [$team, $user]; +} + +beforeEach(function () { + InstanceSettings::query()->delete(); + $settings = new InstanceSettings; + $settings->id = 0; + $settings->save(); + + [$this->team, $this->user] = makeTeamUser(); + + $this->server = Server::factory()->create(['team_id' => $this->team->id]); + $this->server->settings->forceFill([ + 'sentinel_token' => encrypt('super-secret-sentinel-token'), + 'sentinel_custom_url' => 'https://sentinel.internal', + 'logdrain_axiom_api_key' => encrypt('axiom-key-secret'), + 'logdrain_newrelic_license_key' => encrypt('newrelic-key-secret'), + 'logdrain_custom_config' => 'custom-config-data', + 'logdrain_custom_config_parser' => 'custom-parser-data', + ])->saveQuietly(); +}); + +describe('GET /api/v1/servers sensitive field gating', function () { + test('read token does not leak sentinel or logdrain fields', function () { + $token = makeApiToken($this->user, $this->team, ['read']); + + $response = $this->withHeaders([ + 'Authorization' => 'Bearer '.$token, + ])->getJson('/api/v1/servers'); + + $response->assertStatus(200); + + $body = $response->getContent(); + expect($body)->not->toContain('sentinel_token'); + expect($body)->not->toContain('sentinel_custom_url'); + expect($body)->not->toContain('logdrain_axiom_api_key'); + expect($body)->not->toContain('logdrain_newrelic_license_key'); + expect($body)->not->toContain('logdrain_custom_config'); + }); + + test('read sensitive token sees sentinel and logdrain fields', function () { + $token = makeApiToken($this->user, $this->team, ['read', 'read:sensitive']); + + $response = $this->withHeaders([ + 'Authorization' => 'Bearer '.$token, + ])->getJson('/api/v1/servers'); + + $response->assertStatus(200); + + $body = $response->getContent(); + expect($body)->toContain('sentinel_token'); + expect($body)->toContain('sentinel_custom_url'); + expect($body)->toContain('logdrain_axiom_api_key'); + }); + + test('root token sees sentinel and logdrain fields', function () { + $token = makeApiToken($this->user, $this->team, ['root']); + + $response = $this->withHeaders([ + 'Authorization' => 'Bearer '.$token, + ])->getJson('/api/v1/servers'); + + $response->assertStatus(200); + + $body = $response->getContent(); + expect($body)->toContain('sentinel_token'); + }); +}); + +describe('GET /api/v1/applications nested-relation scrubbing', function () { + beforeEach(function () { + $this->project = Project::factory()->create(['team_id' => $this->team->id]); + $this->environment = Environment::factory()->create(['project_id' => $this->project->id]); + $destination = $this->server->standaloneDockers()->firstOrFail(); + + $this->application = Application::create([ + 'name' => 'sensitive-test-app', + 'git_repository' => 'https://github.com/test/test', + 'git_branch' => 'main', + 'build_pack' => 'nixpacks', + 'ports_exposes' => '3000', + 'environment_id' => $this->environment->id, + 'destination_id' => $destination->id, + 'destination_type' => $destination->getMorphClass(), + ]); + }); + + test('read token does not leak sentinel_token via destination.server.settings', function () { + $token = makeApiToken($this->user, $this->team, ['read']); + + $response = $this->withHeaders([ + 'Authorization' => 'Bearer '.$token, + ])->getJson('/api/v1/applications'); + + $response->assertStatus(200); + + $body = $response->getContent(); + expect($body)->not->toContain('sentinel_token'); + expect($body)->not->toContain('sentinel_custom_url'); + expect($body)->not->toContain('logdrain_axiom_api_key'); + }); +}); + +describe('GET /api/v1/databases sensitive field gating', function () { + beforeEach(function () { + $this->project = Project::factory()->create(['team_id' => $this->team->id]); + $this->environment = Environment::factory()->create(['project_id' => $this->project->id]); + $destination = $this->server->standaloneDockers()->firstOrFail(); + + $this->database = StandalonePostgresql::create([ + 'name' => 'sensitive-db', + 'description' => 'test', + 'postgres_user' => 'postgres', + 'postgres_password' => encrypt('super-secret-db-password'), + 'postgres_db' => 'app', + 'image' => 'postgres:16-alpine', + 'environment_id' => $this->environment->id, + 'destination_id' => $destination->id, + 'destination_type' => $destination->getMorphClass(), + ]); + }); + + test('read token does not leak postgres_password or db urls', function () { + $token = makeApiToken($this->user, $this->team, ['read']); + + $response = $this->withHeaders([ + 'Authorization' => 'Bearer '.$token, + ])->getJson('/api/v1/databases'); + + $response->assertStatus(200); + + $body = $response->getContent(); + expect($body)->not->toContain('postgres_password'); + expect($body)->not->toContain('internal_db_url'); + expect($body)->not->toContain('external_db_url'); + }); + + test('read sensitive token sees postgres_password', function () { + $token = makeApiToken($this->user, $this->team, ['read', 'read:sensitive']); + + $response = $this->withHeaders([ + 'Authorization' => 'Bearer '.$token, + ])->getJson('/api/v1/databases'); + + $response->assertStatus(200); + + $body = $response->getContent(); + expect($body)->toContain('postgres_password'); + expect($body)->toContain('internal_db_url'); + }); +}); diff --git a/tests/Unit/Models/SensitiveFieldsHiddenTest.php b/tests/Unit/Models/SensitiveFieldsHiddenTest.php new file mode 100644 index 000000000..92e73c907 --- /dev/null +++ b/tests/Unit/Models/SensitiveFieldsHiddenTest.php @@ -0,0 +1,179 @@ +getHidden(); + + expect($hidden)->toContain( + 'sentinel_token', + 'sentinel_custom_url', + 'logdrain_newrelic_license_key', + 'logdrain_axiom_api_key', + 'logdrain_custom_config', + 'logdrain_custom_config_parser', + ); + }); + + test('Server hides logdrain api keys', function () { + $hidden = (new Server)->getHidden(); + + expect($hidden)->toContain( + 'logdrain_axiom_api_key', + 'logdrain_newrelic_license_key', + ); + }); + + test('Application hides webhook secrets, dockerfile, compose, and labels', function () { + $hidden = (new Application)->getHidden(); + + expect($hidden)->toContain( + 'http_basic_auth_password', + 'manual_webhook_secret_github', + 'manual_webhook_secret_gitlab', + 'manual_webhook_secret_bitbucket', + 'manual_webhook_secret_gitea', + 'dockerfile', + 'docker_compose', + 'docker_compose_raw', + 'custom_labels', + ); + }); + + test('EnvironmentVariable hides value and real_value', function () { + $hidden = (new EnvironmentVariable)->getHidden(); + + expect($hidden)->toContain('value', 'real_value'); + }); + + test('Service hides docker_compose and docker_compose_raw', function () { + $hidden = (new Service)->getHidden(); + + expect($hidden)->toContain('docker_compose', 'docker_compose_raw'); + }); + + test('StandalonePostgresql hides password, init_scripts, db urls', function () { + $hidden = (new StandalonePostgresql)->getHidden(); + + expect($hidden)->toContain( + 'postgres_password', + 'init_scripts', + 'internal_db_url', + 'external_db_url', + ); + }); + + test('StandaloneMysql hides passwords and db urls', function () { + $hidden = (new StandaloneMysql)->getHidden(); + + expect($hidden)->toContain( + 'mysql_password', + 'mysql_root_password', + 'internal_db_url', + 'external_db_url', + ); + }); + + test('StandaloneMariadb hides passwords and db urls', function () { + $hidden = (new StandaloneMariadb)->getHidden(); + + expect($hidden)->toContain( + 'mariadb_password', + 'mariadb_root_password', + 'internal_db_url', + 'external_db_url', + ); + }); + + test('StandaloneMongodb hides root password and db urls', function () { + $hidden = (new StandaloneMongodb)->getHidden(); + + expect($hidden)->toContain( + 'mongo_initdb_root_password', + 'internal_db_url', + 'external_db_url', + ); + }); + + test('StandaloneRedis hides password and db urls', function () { + $hidden = (new StandaloneRedis)->getHidden(); + + expect($hidden)->toContain( + 'redis_password', + 'internal_db_url', + 'external_db_url', + ); + }); + + test('StandaloneClickhouse hides password and db urls', function () { + $hidden = (new StandaloneClickhouse)->getHidden(); + + expect($hidden)->toContain( + 'clickhouse_admin_password', + 'internal_db_url', + 'external_db_url', + ); + }); + + test('StandaloneKeydb hides password and db urls', function () { + $hidden = (new StandaloneKeydb)->getHidden(); + + expect($hidden)->toContain( + 'keydb_password', + 'internal_db_url', + 'external_db_url', + ); + }); + + test('StandaloneDragonfly hides password and db urls', function () { + $hidden = (new StandaloneDragonfly)->getHidden(); + + expect($hidden)->toContain( + 'dragonfly_password', + 'internal_db_url', + 'external_db_url', + ); + }); +}); + +describe('Sensitive fields are absent from toArray() by default', function () { + test('ServerSetting::toArray() excludes sentinel_custom_url by default', function () { + $setting = new ServerSetting; + $setting->setRawAttributes([ + 'server_id' => 1, + 'sentinel_custom_url' => 'https://secret.example.com', + 'wildcard_domain' => 'public.example.com', + ], sync: true); + + $array = $setting->toArray(); + + expect($array)->not->toHaveKey('sentinel_custom_url'); + expect($array)->toHaveKey('wildcard_domain'); + }); + + test('ServerSetting::toArray() includes sentinel_custom_url after makeVisible', function () { + $setting = new ServerSetting; + $setting->setRawAttributes([ + 'server_id' => 1, + 'sentinel_custom_url' => 'https://secret.example.com', + ], sync: true); + + $setting->makeVisible(['sentinel_custom_url']); + $array = $setting->toArray(); + + expect($array['sentinel_custom_url'])->toBe('https://secret.example.com'); + }); +}); From 8dc79f4ed68b98ae3f9f772725f0357224093b18 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Thu, 30 Apr 2026 11:49:15 +0200 Subject: [PATCH 02/12] fix(api): expose nested server secrets for privileged tokens Add `exposeNestedServerSecrets()` to Applications, Databases, and Services controllers so that `read:sensitive`/`root` tokens see sentinel and logdrain fields on eager-loaded Server + ServerSetting relations. ServicesController handles both single models and Eloquent Collections (listing endpoint passes a Collection per project). Tests tightened to use JSON-key assertions (`"field":`) to avoid false positives from field names appearing in values. --- .../Api/ApplicationsController.php | 29 ++++++++++++++ .../Controllers/Api/DatabasesController.php | 28 ++++++++++++++ .../Controllers/Api/ServicesController.php | 38 +++++++++++++++++++ .../Security/ApiSensitiveFieldsTest.php | 27 ++++++++++--- 4 files changed, 116 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/Api/ApplicationsController.php b/app/Http/Controllers/Api/ApplicationsController.php index 9d9c5d33a..e189e6baa 100644 --- a/app/Http/Controllers/Api/ApplicationsController.php +++ b/app/Http/Controllers/Api/ApplicationsController.php @@ -57,6 +57,7 @@ private function removeSensitiveData($application) 'value', 'real_value', ]); + $this->exposeNestedServerSecrets($application); } else { $application->makeHidden([ 'private_key_id', @@ -66,6 +67,34 @@ private function removeSensitiveData($application) return serializeApiResponse($application); } + /** + * Expose sensitive fields on eager-loaded nested Server + ServerSetting + * relations for callers with the `read:sensitive` or `root` token ability. + * Models hide these by default via $hidden; this re-exposes them per-request. + */ + private function exposeNestedServerSecrets($model): void + { + $server = $model->destination?->server ?? null; + if (! $server) { + return; + } + $server->makeVisible([ + 'logdrain_axiom_api_key', + 'logdrain_newrelic_license_key', + ]); + $settings = $server->settings ?? null; + if ($settings) { + $settings->makeVisible([ + 'sentinel_token', + 'sentinel_custom_url', + 'logdrain_newrelic_license_key', + 'logdrain_axiom_api_key', + 'logdrain_custom_config', + 'logdrain_custom_config_parser', + ]); + } + } + #[OA\Get( summary: 'List', description: 'List all applications.', diff --git a/app/Http/Controllers/Api/DatabasesController.php b/app/Http/Controllers/Api/DatabasesController.php index acfc64355..d09c309f8 100644 --- a/app/Http/Controllers/Api/DatabasesController.php +++ b/app/Http/Controllers/Api/DatabasesController.php @@ -49,11 +49,39 @@ private function removeSensitiveData($database) 'mariadb_password', 'mariadb_root_password', ]); + $this->exposeNestedServerSecrets($database); } return serializeApiResponse($database); } + /** + * Expose sensitive fields on eager-loaded nested Server + ServerSetting + * relations for callers with the `read:sensitive` or `root` token ability. + */ + private function exposeNestedServerSecrets($model): void + { + $server = $model->destination?->server ?? null; + if (! $server) { + return; + } + $server->makeVisible([ + 'logdrain_axiom_api_key', + 'logdrain_newrelic_license_key', + ]); + $settings = $server->settings ?? null; + if ($settings) { + $settings->makeVisible([ + 'sentinel_token', + 'sentinel_custom_url', + 'logdrain_newrelic_license_key', + 'logdrain_axiom_api_key', + 'logdrain_custom_config', + 'logdrain_custom_config_parser', + ]); + } + } + #[OA\Get( summary: 'List', description: 'List all databases.', diff --git a/app/Http/Controllers/Api/ServicesController.php b/app/Http/Controllers/Api/ServicesController.php index 4a04f499d..c3ced0e31 100644 --- a/app/Http/Controllers/Api/ServicesController.php +++ b/app/Http/Controllers/Api/ServicesController.php @@ -16,6 +16,7 @@ use App\Support\ValidationPatterns; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Validator; use OpenApi\Attributes as OA; use Symfony\Component\Yaml\Yaml; @@ -37,11 +38,48 @@ private function removeSensitiveData($service) 'value', 'real_value', ]); + $this->exposeNestedServerSecrets($service); } return serializeApiResponse($service); } + /** + * Expose sensitive fields on eager-loaded nested Server + ServerSetting + * relations for callers with the `read:sensitive` or `root` token ability. + * Handles both single models and Eloquent Collections (the listing endpoint + * passes a Collection of Services per project to removeSensitiveData()). + */ + private function exposeNestedServerSecrets($model): void + { + if ($model instanceof Collection || $model instanceof \Illuminate\Database\Eloquent\Collection) { + foreach ($model as $item) { + $this->exposeNestedServerSecrets($item); + } + + return; + } + $server = $model->destination?->server ?? $model->server ?? null; + if (! $server) { + return; + } + $server->makeVisible([ + 'logdrain_axiom_api_key', + 'logdrain_newrelic_license_key', + ]); + $settings = $server->settings ?? null; + if ($settings) { + $settings->makeVisible([ + 'sentinel_token', + 'sentinel_custom_url', + 'logdrain_newrelic_license_key', + 'logdrain_axiom_api_key', + 'logdrain_custom_config', + 'logdrain_custom_config_parser', + ]); + } + } + private function applyServiceUrls(Service $service, array $urlsArray, string $teamId, bool $forceDomainOverride = false): ?array { $errors = []; diff --git a/tests/Feature/Security/ApiSensitiveFieldsTest.php b/tests/Feature/Security/ApiSensitiveFieldsTest.php index b71881e3b..2275f7cc2 100644 --- a/tests/Feature/Security/ApiSensitiveFieldsTest.php +++ b/tests/Feature/Security/ApiSensitiveFieldsTest.php @@ -128,9 +128,23 @@ function makeTeamUser(): array $response->assertStatus(200); $body = $response->getContent(); - expect($body)->not->toContain('sentinel_token'); - expect($body)->not->toContain('sentinel_custom_url'); - expect($body)->not->toContain('logdrain_axiom_api_key'); + expect($body)->not->toContain('"sentinel_token":'); + expect($body)->not->toContain('"sentinel_custom_url":'); + expect($body)->not->toContain('"logdrain_axiom_api_key":'); + }); + + test('read-sensitive token sees nested sentinel_token via destination.server.settings', function () { + $token = makeApiToken($this->user, $this->team, ['read', 'read:sensitive']); + + $response = $this->withHeaders([ + 'Authorization' => 'Bearer '.$token, + ])->getJson('/api/v1/applications'); + + $response->assertStatus(200); + + $body = $response->getContent(); + expect($body)->toContain('"sentinel_token":'); + expect($body)->toContain('"sentinel_custom_url":'); }); }); @@ -168,7 +182,7 @@ function makeTeamUser(): array expect($body)->not->toContain('external_db_url'); }); - test('read sensitive token sees postgres_password', function () { + test('read sensitive token sees postgres_password and nested sentinel_token', function () { $token = makeApiToken($this->user, $this->team, ['read', 'read:sensitive']); $response = $this->withHeaders([ @@ -178,7 +192,8 @@ function makeTeamUser(): array $response->assertStatus(200); $body = $response->getContent(); - expect($body)->toContain('postgres_password'); - expect($body)->toContain('internal_db_url'); + expect($body)->toContain('"postgres_password":'); + expect($body)->toContain('"internal_db_url":'); + expect($body)->toContain('"sentinel_token":'); }); }); From c97f9160528d1a242c7991696a549f92d57a9ff9 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 11 May 2026 11:46:05 +0200 Subject: [PATCH 03/12] fix(api): hide application compose PR fields Add compose PR fields to the Application hidden attributes and allow database list queries to eager load nested server settings when sensitive access is permitted. --- app/Http/Controllers/Api/DatabasesController.php | 9 +++++++-- app/Models/Application.php | 2 ++ app/Models/Project.php | 12 ++++++++++-- templates/service-templates-latest.json | 10 +++++----- templates/service-templates.json | 10 +++++----- tests/Feature/Security/ApiSensitiveFieldsTest.php | 10 ++++++++++ tests/Unit/Models/SensitiveFieldsHiddenTest.php | 2 ++ 7 files changed, 41 insertions(+), 14 deletions(-) diff --git a/app/Http/Controllers/Api/DatabasesController.php b/app/Http/Controllers/Api/DatabasesController.php index d09c309f8..839e52cb3 100644 --- a/app/Http/Controllers/Api/DatabasesController.php +++ b/app/Http/Controllers/Api/DatabasesController.php @@ -20,6 +20,7 @@ use App\Models\Server; use App\Models\StandalonePostgresql; use App\Support\ValidationPatterns; +use Illuminate\Database\Eloquent\Model; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; @@ -59,7 +60,7 @@ private function removeSensitiveData($database) * Expose sensitive fields on eager-loaded nested Server + ServerSetting * relations for callers with the `read:sensitive` or `root` token ability. */ - private function exposeNestedServerSecrets($model): void + private function exposeNestedServerSecrets(Model $model): void { $server = $model->destination?->server ?? null; if (! $server) { @@ -118,8 +119,12 @@ public function databases(Request $request) } $projects = Project::where('team_id', $teamId)->get(); $databases = collect(); + $databaseRelations = $request->attributes->get('can_read_sensitive', false) === true + ? ['destination.server.settings'] + : []; + foreach ($projects as $project) { - $databases = $databases->merge($project->databases()); + $databases = $databases->merge($project->databases($databaseRelations)); } $databaseIds = $databases->pluck('id')->toArray(); diff --git a/app/Models/Application.php b/app/Models/Application.php index f342112b9..6f2275ba8 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -229,7 +229,9 @@ class Application extends BaseModel 'manual_webhook_secret_gitea', 'dockerfile', 'docker_compose', + 'docker_compose_pr', 'docker_compose_raw', + 'docker_compose_pr_raw', 'custom_labels', ]; diff --git a/app/Models/Project.php b/app/Models/Project.php index 632787a07..236ef3a26 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -5,6 +5,7 @@ use App\Traits\ClearsGlobalSearchCache; use App\Traits\HasSafeStringAttribute; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Support\Collection; use OpenApi\Attributes as OA; use Visus\Cuid2\Cuid2; @@ -156,9 +157,16 @@ public function isEmpty() $this->services()->count() == 0; } - public function databases() + public function databases(array $with = []): Collection { - return $this->postgresqls()->get()->merge($this->redis()->get())->merge($this->mongodbs()->get())->merge($this->mysqls()->get())->merge($this->mariadbs()->get())->merge($this->keydbs()->get())->merge($this->dragonflies()->get())->merge($this->clickhouses()->get()); + return $this->postgresqls()->with($with)->get() + ->merge($this->redis()->with($with)->get()) + ->merge($this->mongodbs()->with($with)->get()) + ->merge($this->mysqls()->with($with)->get()) + ->merge($this->mariadbs()->with($with)->get()) + ->merge($this->keydbs()->with($with)->get()) + ->merge($this->dragonflies()->with($with)->get()) + ->merge($this->clickhouses()->with($with)->get()); } public function navigateTo() diff --git a/templates/service-templates-latest.json b/templates/service-templates-latest.json index eb667fcb8..b57d9d29c 100644 --- a/templates/service-templates-latest.json +++ b/templates/service-templates-latest.json @@ -299,7 +299,7 @@ "bluesky-pds": { "documentation": "https://github.com/bluesky-social/pds?utm_source=coolify.io", "slogan": "Bluesky PDS (Personal Data Server)", - "compose": "c2VydmljZXM6CiAgcGRzOgogICAgaW1hZ2U6ICdnaGNyLmlvL2JsdWVza3ktc29jaWFsL3BkczowLjQuMTgyJwogICAgdm9sdW1lczoKICAgICAgLSAncGRzLWRhdGE6L3BkcycKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfVVJMX1BEU18zMDAwCiAgICAgIC0gJ1BEU19IT1NUTkFNRT0ke1NFUlZJQ0VfRlFETl9QRFN9JwogICAgICAtICdQRFNfSldUX1NFQ1JFVD0ke1NFUlZJQ0VfSEVYXzMyX0pXVFNFQ1JFVH0nCiAgICAgIC0gJ1BEU19BRE1JTl9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfQURNSU59JwogICAgICAtICdQRFNfQURNSU5fRU1BSUw9JHtQRFNfQURNSU5fRU1BSUx9JwogICAgICAtICdQRFNfUExDX1JPVEFUSU9OX0tFWV9LMjU2X1BSSVZBVEVfS0VZX0hFWD0ke1NFUlZJQ0VfSEVYXzMyX1JPVEFUSU9OS0VZfScKICAgICAgLSAnUERTX0RBVEFfRElSRUNUT1JZPSR7UERTX0RBVEFfRElSRUNUT1JZOi0vcGRzfScKICAgICAgLSAnUERTX0JMT0JTVE9SRV9ESVNLX0xPQ0FUSU9OPSR7UERTX0RBVEFfRElSRUNUT1JZOi0vcGRzfS9ibG9ja3MnCiAgICAgIC0gJ1BEU19CTE9CX1VQTE9BRF9MSU1JVD0ke1BEU19CTE9CX1VQTE9BRF9MSU1JVDotMTA0ODU3NjAwfScKICAgICAgLSAnUERTX0RJRF9QTENfVVJMPSR7UERTX0RJRF9QTENfVVJMOi1odHRwczovL3BsYy5kaXJlY3Rvcnl9JwogICAgICAtICdQRFNfRU1BSUxfRlJPTV9BRERSRVNTPSR7UERTX0VNQUlMX0ZST01fQUREUkVTU30nCiAgICAgIC0gJ1BEU19FTUFJTF9TTVRQX1VSTD0ke1BEU19FTUFJTF9TTVRQX1VSTH0nCiAgICAgIC0gJ1BEU19CU0tZX0FQUF9WSUVXX1VSTD0ke1BEU19CU0tZX0FQUF9WSUVXX1VSTDotaHR0cHM6Ly9hcGkuYnNreS5hcHB9JwogICAgICAtICdQRFNfQlNLWV9BUFBfVklFV19ESUQ9JHtQRFNfQlNLWV9BUFBfVklFV19ESUQ6LWRpZDp3ZWI6YXBpLmJza3kuYXBwfScKICAgICAgLSAnUERTX1JFUE9SVF9TRVJWSUNFX1VSTD0ke1BEU19SRVBPUlRfU0VSVklDRV9VUkw6LWh0dHBzOi8vbW9kLmJza3kuYXBwL3hycGMvY29tLmF0cHJvdG8ubW9kZXJhdGlvbi5jcmVhdGVSZXBvcnR9JwogICAgICAtICdQRFNfUkVQT1JUX1NFUlZJQ0VfRElEPSR7UERTX1JFUE9SVF9TRVJWSUNFX0RJRDotZGlkOnBsYzphcjdjNGJ5NDZxamR5ZGhkZXZ2cm5kYWN9JwogICAgICAtICdQRFNfQ1JBV0xFUlM9JHtQRFNfQ1JBV0xFUlM6LWh0dHBzOi8vYnNreS5uZXR3b3JrfScKICAgICAgLSAnTE9HX0VOQUJMRUQ9JHtMT0dfRU5BQkxFRDotdHJ1ZX0nCiAgICBjb21tYW5kOiAic2ggLWMgJ1xuICBzZXQgLWV1byBwaXBlZmFpbFxuICBlY2hvIFwiSW5zdGFsbGluZyByZXF1aXJlZCBwYWNrYWdlcyBhbmQgcGRzYWRtaW4uLi5cIlxuICBhcGsgYWRkIC0tbm8tY2FjaGUgb3BlbnNzbCBjdXJsIGJhc2gganEgY29yZXV0aWxzIGdudXBnIHV0aWwtbGludXgtbWlzYyA+L2Rldi9udWxsXG4gIGN1cmwgLW8gL3Vzci9sb2NhbC9iaW4vcGRzYWRtaW4uc2ggaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2JsdWVza3ktc29jaWFsL3Bkcy9tYWluL3Bkc2FkbWluLnNoXG4gIGNobW9kIDcwMCAvdXNyL2xvY2FsL2Jpbi9wZHNhZG1pbi5zaFxuICBsbiAtc2YgL3Vzci9sb2NhbC9iaW4vcGRzYWRtaW4uc2ggL3Vzci9sb2NhbC9iaW4vcGRzYWRtaW5cbiAgZWNobyBcIkNyZWF0aW5nIGFuIGVtcHR5IHBkcy5lbnYgZmlsZSBzbyBwZHNhZG1pbiB3b3Jrcy4uLlwiXG4gIHRvdWNoICR7UERTX0RBVEFfRElSRUNUT1JZfS9wZHMuZW52XG4gIGVjaG8gXCJMYXVuY2hpbmcgUERTLCBlbmpveSEuLi5cIlxuICBleGVjIG5vZGUgLS1lbmFibGUtc291cmNlLW1hcHMgaW5kZXguanNcbidcbiIKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSB3Z2V0CiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwL3hycGMvX2hlYWx0aCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxMAo=", + "compose": "c2VydmljZXM6CiAgcGRzOgogICAgaW1hZ2U6ICdnaGNyLmlvL2JsdWVza3ktc29jaWFsL3BkczowLjQuMTgyJwogICAgdm9sdW1lczoKICAgICAgLSAncGRzLWRhdGE6L3BkcycKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfVVJMX1BEU18zMDAwCiAgICAgIC0gJ1BEU19IT1NUTkFNRT0ke1NFUlZJQ0VfRlFETl9QRFN9JwogICAgICAtICdQRFNfSldUX1NFQ1JFVD0ke1NFUlZJQ0VfSEVYXzY0X0pXVFNFQ1JFVH0nCiAgICAgIC0gJ1BEU19BRE1JTl9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfQURNSU59JwogICAgICAtICdQRFNfQURNSU5fRU1BSUw9JHtQRFNfQURNSU5fRU1BSUx9JwogICAgICAtICdQRFNfUExDX1JPVEFUSU9OX0tFWV9LMjU2X1BSSVZBVEVfS0VZX0hFWD0ke1NFUlZJQ0VfSEVYXzY0X1JPVEFUSU9OS0VZfScKICAgICAgLSAnUERTX0RBVEFfRElSRUNUT1JZPSR7UERTX0RBVEFfRElSRUNUT1JZOi0vcGRzfScKICAgICAgLSAnUERTX0JMT0JTVE9SRV9ESVNLX0xPQ0FUSU9OPSR7UERTX0RBVEFfRElSRUNUT1JZOi0vcGRzfS9ibG9ja3MnCiAgICAgIC0gJ1BEU19CTE9CX1VQTE9BRF9MSU1JVD0ke1BEU19CTE9CX1VQTE9BRF9MSU1JVDotMTA0ODU3NjAwfScKICAgICAgLSAnUERTX0RJRF9QTENfVVJMPSR7UERTX0RJRF9QTENfVVJMOi1odHRwczovL3BsYy5kaXJlY3Rvcnl9JwogICAgICAtICdQRFNfRU1BSUxfRlJPTV9BRERSRVNTPSR7UERTX0VNQUlMX0ZST01fQUREUkVTU30nCiAgICAgIC0gJ1BEU19FTUFJTF9TTVRQX1VSTD0ke1BEU19FTUFJTF9TTVRQX1VSTH0nCiAgICAgIC0gJ1BEU19CU0tZX0FQUF9WSUVXX1VSTD0ke1BEU19CU0tZX0FQUF9WSUVXX1VSTDotaHR0cHM6Ly9hcGkuYnNreS5hcHB9JwogICAgICAtICdQRFNfQlNLWV9BUFBfVklFV19ESUQ9JHtQRFNfQlNLWV9BUFBfVklFV19ESUQ6LWRpZDp3ZWI6YXBpLmJza3kuYXBwfScKICAgICAgLSAnUERTX1JFUE9SVF9TRVJWSUNFX1VSTD0ke1BEU19SRVBPUlRfU0VSVklDRV9VUkw6LWh0dHBzOi8vbW9kLmJza3kuYXBwL3hycGMvY29tLmF0cHJvdG8ubW9kZXJhdGlvbi5jcmVhdGVSZXBvcnR9JwogICAgICAtICdQRFNfUkVQT1JUX1NFUlZJQ0VfRElEPSR7UERTX1JFUE9SVF9TRVJWSUNFX0RJRDotZGlkOnBsYzphcjdjNGJ5NDZxamR5ZGhkZXZ2cm5kYWN9JwogICAgICAtICdQRFNfQ1JBV0xFUlM9JHtQRFNfQ1JBV0xFUlM6LWh0dHBzOi8vYnNreS5uZXR3b3JrfScKICAgICAgLSAnTE9HX0VOQUJMRUQ9JHtMT0dfRU5BQkxFRDotdHJ1ZX0nCiAgICBjb21tYW5kOiAic2ggLWMgJ1xuICBzZXQgLWV1byBwaXBlZmFpbFxuICBlY2hvIFwiSW5zdGFsbGluZyByZXF1aXJlZCBwYWNrYWdlcyBhbmQgcGRzYWRtaW4uLi5cIlxuICBhcGsgYWRkIC0tbm8tY2FjaGUgb3BlbnNzbCBjdXJsIGJhc2gganEgY29yZXV0aWxzIGdudXBnIHV0aWwtbGludXgtbWlzYyA+L2Rldi9udWxsXG4gIGN1cmwgLW8gL3Vzci9sb2NhbC9iaW4vcGRzYWRtaW4uc2ggaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2JsdWVza3ktc29jaWFsL3Bkcy9tYWluL3Bkc2FkbWluLnNoXG4gIGNobW9kIDcwMCAvdXNyL2xvY2FsL2Jpbi9wZHNhZG1pbi5zaFxuICBsbiAtc2YgL3Vzci9sb2NhbC9iaW4vcGRzYWRtaW4uc2ggL3Vzci9sb2NhbC9iaW4vcGRzYWRtaW5cbiAgZWNobyBcIkNyZWF0aW5nIGFuIGVtcHR5IHBkcy5lbnYgZmlsZSBzbyBwZHNhZG1pbiB3b3Jrcy4uLlwiXG4gIHRvdWNoICR7UERTX0RBVEFfRElSRUNUT1JZfS9wZHMuZW52XG4gIGVjaG8gXCJMYXVuY2hpbmcgUERTLCBlbmpveSEuLi5cIlxuICBleGVjIG5vZGUgLS1lbmFibGUtc291cmNlLW1hcHMgaW5kZXguanNcbidcbiIKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSB3Z2V0CiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwL3hycGMvX2hlYWx0aCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxMAo=", "tags": [ "bluesky", "pds", @@ -730,7 +730,7 @@ "convex": { "documentation": "https://github.com/get-convex/convex-backend/blob/main/self-hosted/README.md?utm_source=coolify.io", "slogan": "Convex is the open-source reactive database for app developers.", - "compose": "c2VydmljZXM6CiAgYmFja2VuZDoKICAgIGltYWdlOiAnZ2hjci5pby9nZXQtY29udmV4L2NvbnZleC1iYWNrZW5kOmE5YTc2MGNhMTAzOTllZDQyZTFiNGJiODdjNzg1MzlhMjM1NDg4YzcnCiAgICB2b2x1bWVzOgogICAgICAtICdkYXRhOi9jb252ZXgvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfVVJMX0JBQ0tFTkRfMzIxMAogICAgICAtICdJTlNUQU5DRV9OQU1FPSR7SU5TVEFOQ0VfTkFNRTotc2VsZi1ob3N0ZWQtY29udmV4fScKICAgICAgLSAnSU5TVEFOQ0VfU0VDUkVUPSR7U0VSVklDRV9IRVhfMzJfU0VDUkVUfScKICAgICAgLSAnQ09OVkVYX1JFTEVBU0VfVkVSU0lPTl9ERVY9JHtDT05WRVhfUkVMRUFTRV9WRVJTSU9OX0RFVjotfScKICAgICAgLSAnQUNUSU9OU19VU0VSX1RJTUVPVVRfU0VDUz0ke0FDVElPTlNfVVNFUl9USU1FT1VUX1NFQ1M6LX0nCiAgICAgIC0gJ0NPTlZFWF9DTE9VRF9PUklHSU49JHtTRVJWSUNFX1VSTF9EQVNIQk9BUkR9JwogICAgICAtICdDT05WRVhfU0lURV9PUklHSU49JHtTRVJWSUNFX1VSTF9CQUNLRU5EfScKICAgICAgLSAnREFUQUJBU0VfVVJMPSR7REFUQUJBU0VfVVJMOi19JwogICAgICAtICdESVNBQkxFX0JFQUNPTj0ke0RJU0FCTEVfQkVBQ09OOj9mYWxzZX0nCiAgICAgIC0gJ1JFREFDVF9MT0dTX1RPX0NMSUVOVD0ke1JFREFDVF9MT0dTX1RPX0NMSUVOVDo/ZmFsc2V9JwogICAgICAtICdET19OT1RfUkVRVUlSRV9TU0w9JHtET19OT1RfUkVRVUlSRV9TU0w6P3RydWV9JwogICAgICAtICdQT1NUR1JFU19VUkw9JHtQT1NUR1JFU19VUkw6LX0nCiAgICAgIC0gJ01ZU1FMX1VSTD0ke01ZU1FMX1VSTDotfScKICAgICAgLSAnUlVTVF9MT0c9JHtSVVNUX0xPRzotaW5mb30nCiAgICAgIC0gJ1JVU1RfQkFDS1RSQUNFPSR7UlVTVF9CQUNLVFJBQ0U6LX0nCiAgICAgIC0gJ0FXU19SRUdJT049JHtBV1NfUkVHSU9OOi19JwogICAgICAtICdBV1NfQUNDRVNTX0tFWV9JRD0ke0FXU19BQ0NFU1NfS0VZX0lEOi19JwogICAgICAtICdBV1NfU0VDUkVUX0FDQ0VTU19LRVk9JHtBV1NfU0VDUkVUX0FDQ0VTU19LRVk6LX0nCiAgICAgIC0gJ0FXU19TRVNTSU9OX1RPS0VOPSR7QVdTX1NFU1NJT05fVE9LRU46LX0nCiAgICAgIC0gJ0FXU19TM19GT1JDRV9QQVRIX1NUWUxFPSR7QVdTX1MzX0ZPUkNFX1BBVEhfU1RZTEU6LX0nCiAgICAgIC0gJ0FXU19TM19ESVNBQkxFX1NTRT0ke0FXU19TM19ESVNBQkxFX1NTRTotfScKICAgICAgLSAnQVdTX1MzX0RJU0FCTEVfQ0hFQ0tTVU1TPSR7QVdTX1MzX0RJU0FCTEVfQ0hFQ0tTVU1TOi19JwogICAgICAtICdTM19TVE9SQUdFX0VYUE9SVFNfQlVDS0VUPSR7UzNfU1RPUkFHRV9FWFBPUlRTX0JVQ0tFVDotfScKICAgICAgLSAnUzNfU1RPUkFHRV9TTkFQU0hPVF9JTVBPUlRTX0JVQ0tFVD0ke1MzX1NUT1JBR0VfU05BUFNIT1RfSU1QT1JUU19CVUNLRVQ6LX0nCiAgICAgIC0gJ1MzX1NUT1JBR0VfTU9EVUxFU19CVUNLRVQ9JHtTM19TVE9SQUdFX01PRFVMRVNfQlVDS0VUOi19JwogICAgICAtICdTM19TVE9SQUdFX0ZJTEVTX0JVQ0tFVD0ke1MzX1NUT1JBR0VfRklMRVNfQlVDS0VUOi19JwogICAgICAtICdTM19TVE9SQUdFX1NFQVJDSF9CVUNLRVQ9JHtTM19TVE9SQUdFX1NFQVJDSF9CVUNLRVQ6LX0nCiAgICAgIC0gJ1MzX0VORFBPSU5UX1VSTD0ke1MzX0VORFBPSU5UX1VSTDotfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiAnY3VybCAtZiBodHRwOi8vMTI3LjAuMC4xOjMyMTAvdmVyc2lvbicKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHN0YXJ0X3BlcmlvZDogMTBzCiAgZGFzaGJvYXJkOgogICAgaW1hZ2U6ICdnaGNyLmlvL2dldC1jb252ZXgvY29udmV4LWRhc2hib2FyZDphOWE3NjBjYTEwMzk5ZWQ0MmUxYjRiYjg3Yzc4NTM5YTIzNTQ4OGM3JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9VUkxfREFTSEJPQVJEXzY3OTEKICAgICAgLSAnTkVYVF9QVUJMSUNfREVQTE9ZTUVOVF9VUkw9JHtTRVJWSUNFX1VSTF9CQUNLRU5EfScKICAgIGRlcGVuZHNfb246CiAgICAgIGJhY2tlbmQ6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiAnY3VybCAtZiBodHRwOi8vMTI3LjAuMC4xOjY3OTEvJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgc3RhcnRfcGVyaW9kOiA1cwo=", + "compose": "c2VydmljZXM6CiAgYmFja2VuZDoKICAgIGltYWdlOiAnZ2hjci5pby9nZXQtY29udmV4L2NvbnZleC1iYWNrZW5kOmE5YTc2MGNhMTAzOTllZDQyZTFiNGJiODdjNzg1MzlhMjM1NDg4YzcnCiAgICB2b2x1bWVzOgogICAgICAtICdkYXRhOi9jb252ZXgvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfVVJMX0JBQ0tFTkRfMzIxMAogICAgICAtICdJTlNUQU5DRV9OQU1FPSR7SU5TVEFOQ0VfTkFNRTotc2VsZi1ob3N0ZWQtY29udmV4fScKICAgICAgLSAnSU5TVEFOQ0VfU0VDUkVUPSR7U0VSVklDRV9IRVhfNjRfU0VDUkVUfScKICAgICAgLSAnQ09OVkVYX1JFTEVBU0VfVkVSU0lPTl9ERVY9JHtDT05WRVhfUkVMRUFTRV9WRVJTSU9OX0RFVjotfScKICAgICAgLSAnQUNUSU9OU19VU0VSX1RJTUVPVVRfU0VDUz0ke0FDVElPTlNfVVNFUl9USU1FT1VUX1NFQ1M6LX0nCiAgICAgIC0gJ0NPTlZFWF9DTE9VRF9PUklHSU49JHtTRVJWSUNFX1VSTF9EQVNIQk9BUkR9JwogICAgICAtICdDT05WRVhfU0lURV9PUklHSU49JHtTRVJWSUNFX1VSTF9CQUNLRU5EfScKICAgICAgLSAnREFUQUJBU0VfVVJMPSR7REFUQUJBU0VfVVJMOi19JwogICAgICAtICdESVNBQkxFX0JFQUNPTj0ke0RJU0FCTEVfQkVBQ09OOj9mYWxzZX0nCiAgICAgIC0gJ1JFREFDVF9MT0dTX1RPX0NMSUVOVD0ke1JFREFDVF9MT0dTX1RPX0NMSUVOVDo/ZmFsc2V9JwogICAgICAtICdET19OT1RfUkVRVUlSRV9TU0w9JHtET19OT1RfUkVRVUlSRV9TU0w6P3RydWV9JwogICAgICAtICdQT1NUR1JFU19VUkw9JHtQT1NUR1JFU19VUkw6LX0nCiAgICAgIC0gJ01ZU1FMX1VSTD0ke01ZU1FMX1VSTDotfScKICAgICAgLSAnUlVTVF9MT0c9JHtSVVNUX0xPRzotaW5mb30nCiAgICAgIC0gJ1JVU1RfQkFDS1RSQUNFPSR7UlVTVF9CQUNLVFJBQ0U6LX0nCiAgICAgIC0gJ0FXU19SRUdJT049JHtBV1NfUkVHSU9OOi19JwogICAgICAtICdBV1NfQUNDRVNTX0tFWV9JRD0ke0FXU19BQ0NFU1NfS0VZX0lEOi19JwogICAgICAtICdBV1NfU0VDUkVUX0FDQ0VTU19LRVk9JHtBV1NfU0VDUkVUX0FDQ0VTU19LRVk6LX0nCiAgICAgIC0gJ0FXU19TRVNTSU9OX1RPS0VOPSR7QVdTX1NFU1NJT05fVE9LRU46LX0nCiAgICAgIC0gJ0FXU19TM19GT1JDRV9QQVRIX1NUWUxFPSR7QVdTX1MzX0ZPUkNFX1BBVEhfU1RZTEU6LX0nCiAgICAgIC0gJ0FXU19TM19ESVNBQkxFX1NTRT0ke0FXU19TM19ESVNBQkxFX1NTRTotfScKICAgICAgLSAnQVdTX1MzX0RJU0FCTEVfQ0hFQ0tTVU1TPSR7QVdTX1MzX0RJU0FCTEVfQ0hFQ0tTVU1TOi19JwogICAgICAtICdTM19TVE9SQUdFX0VYUE9SVFNfQlVDS0VUPSR7UzNfU1RPUkFHRV9FWFBPUlRTX0JVQ0tFVDotfScKICAgICAgLSAnUzNfU1RPUkFHRV9TTkFQU0hPVF9JTVBPUlRTX0JVQ0tFVD0ke1MzX1NUT1JBR0VfU05BUFNIT1RfSU1QT1JUU19CVUNLRVQ6LX0nCiAgICAgIC0gJ1MzX1NUT1JBR0VfTU9EVUxFU19CVUNLRVQ9JHtTM19TVE9SQUdFX01PRFVMRVNfQlVDS0VUOi19JwogICAgICAtICdTM19TVE9SQUdFX0ZJTEVTX0JVQ0tFVD0ke1MzX1NUT1JBR0VfRklMRVNfQlVDS0VUOi19JwogICAgICAtICdTM19TVE9SQUdFX1NFQVJDSF9CVUNLRVQ9JHtTM19TVE9SQUdFX1NFQVJDSF9CVUNLRVQ6LX0nCiAgICAgIC0gJ1MzX0VORFBPSU5UX1VSTD0ke1MzX0VORFBPSU5UX1VSTDotfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiAnY3VybCAtZiBodHRwOi8vMTI3LjAuMC4xOjMyMTAvdmVyc2lvbicKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHN0YXJ0X3BlcmlvZDogMTBzCiAgZGFzaGJvYXJkOgogICAgaW1hZ2U6ICdnaGNyLmlvL2dldC1jb252ZXgvY29udmV4LWRhc2hib2FyZDphOWE3NjBjYTEwMzk5ZWQ0MmUxYjRiYjg3Yzc4NTM5YTIzNTQ4OGM3JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9VUkxfREFTSEJPQVJEXzY3OTEKICAgICAgLSAnTkVYVF9QVUJMSUNfREVQTE9ZTUVOVF9VUkw9JHtTRVJWSUNFX1VSTF9CQUNLRU5EfScKICAgIGRlcGVuZHNfb246CiAgICAgIGJhY2tlbmQ6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiAnY3VybCAtZiBodHRwOi8vMTI3LjAuMC4xOjY3OTEvJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgc3RhcnRfcGVyaW9kOiA1cwo=", "tags": [ "database", "reactive", @@ -1608,7 +1608,7 @@ "getoutline": { "documentation": "https://docs.getoutline.com/s/hosting/doc/hosting-outline-nipGaCRBDu?utm_source=coolify.io", "slogan": "Your team\u2019s knowledge base", - "compose": "c2VydmljZXM6CiAgb3V0bGluZToKICAgIGltYWdlOiAnZG9ja2VyLmdldG91dGxpbmUuY29tL291dGxpbmV3aWtpL291dGxpbmU6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnc3RvcmFnZS1kYXRhOi92YXIvbGliL291dGxpbmUvZGF0YScKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHJlZGlzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX1VSTF9PVVRMSU5FXzMwMDAKICAgICAgLSBOT0RFX0VOVj1wcm9kdWN0aW9uCiAgICAgIC0gJ1NFQ1JFVF9LRVk9JHtTRVJWSUNFX0hFWF8zMl9PVVRMSU5FfScKICAgICAgLSAnVVRJTFNfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF82NF9PVVRMSU5FfScKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vJHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9OiR7U0VSVklDRV9QQVNTV09SRF82NF9QT1NUR1JFU31AcG9zdGdyZXM6NTQzMi8ke1BPU1RHUkVTX0RBVEFCQVNFOi1vdXRsaW5lfScKICAgICAgLSAnUkVESVNfVVJMPXJlZGlzOi8vOiR7U0VSVklDRV9QQVNTV09SRF82NF9SRURJU31AcmVkaXM6NjM3OScKICAgICAgLSAnVVJMPSR7U0VSVklDRV9VUkxfT1VUTElORX0nCiAgICAgIC0gJ1BPUlQ9JHtPVVRMSU5FX1BPUlQ6LTMwMDB9JwogICAgICAtICdGSUxFX1NUT1JBR0U9JHtGSUxFX1NUT1JBR0U6LWxvY2FsfScKICAgICAgLSAnRklMRV9TVE9SQUdFX0xPQ0FMX1JPT1RfRElSPSR7RklMRV9TVE9SQUdFX0xPQ0FMX1JPT1RfRElSOi0vdmFyL2xpYi9vdXRsaW5lL2RhdGF9JwogICAgICAtICdGSUxFX1NUT1JBR0VfVVBMT0FEX01BWF9TSVpFPSR7RklMRV9TVE9SQUdFX1VQTE9BRF9NQVhfU0laRTotMjAwMH0nCiAgICAgIC0gJ0ZJTEVfU1RPUkFHRV9JTVBPUlRfTUFYX1NJWkU9JHtGSUxFX1NUT1JBR0VfSU1QT1JUX01BWF9TSVpFOi0xMDB9JwogICAgICAtICdGSUxFX1NUT1JBR0VfV09SS1NQQUNFX0lNUE9SVF9NQVhfU0laRT0ke0ZJTEVfU1RPUkFHRV9XT1JLU1BBQ0VfSU1QT1JUX01BWF9TSVpFfScKICAgICAgLSAnQVdTX0FDQ0VTU19LRVlfSUQ9JHtBV1NfQUNDRVNTX0tFWV9JRH0nCiAgICAgIC0gJ0FXU19TRUNSRVRfQUNDRVNTX0tFWT0ke0FXU19TRUNSRVRfQUNDRVNTX0tFWX0nCiAgICAgIC0gJ0FXU19SRUdJT049JHtBV1NfUkVHSU9OfScKICAgICAgLSAnQVdTX1MzX0FDQ0VMRVJBVEVfVVJMPSR7QVdTX1MzX0FDQ0VMRVJBVEVfVVJMfScKICAgICAgLSAnQVdTX1MzX1VQTE9BRF9CVUNLRVRfVVJMPSR7QVdTX1MzX1VQTE9BRF9CVUNLRVRfVVJMfScKICAgICAgLSAnQVdTX1MzX1VQTE9BRF9CVUNLRVRfTkFNRT0ke0FXU19TM19VUExPQURfQlVDS0VUX05BTUV9JwogICAgICAtICdBV1NfUzNfRk9SQ0VfUEFUSF9TVFlMRT0ke0FXU19TM19GT1JDRV9QQVRIX1NUWUxFOi10cnVlfScKICAgICAgLSAnQVdTX1MzX0FDTD0ke0FXU19TM19BQ0w6LXByaXZhdGV9JwogICAgICAtICdTTEFDS19DTElFTlRfSUQ9JHtTTEFDS19DTElFTlRfSUR9JwogICAgICAtICdTTEFDS19DTElFTlRfU0VDUkVUPSR7U0xBQ0tfQ0xJRU5UX1NFQ1JFVH0nCiAgICAgIC0gJ0dPT0dMRV9DTElFTlRfSUQ9JHtHT09HTEVfQ0xJRU5UX0lEfScKICAgICAgLSAnR09PR0xFX0NMSUVOVF9TRUNSRVQ9JHtHT09HTEVfQ0xJRU5UX1NFQ1JFVH0nCiAgICAgIC0gJ0FaVVJFX0NMSUVOVF9JRD0ke0FaVVJFX0NMSUVOVF9JRH0nCiAgICAgIC0gJ0FaVVJFX0NMSUVOVF9TRUNSRVQ9JHtBWlVSRV9DTElFTlRfU0VDUkVUfScKICAgICAgLSAnQVpVUkVfUkVTT1VSQ0VfQVBQX0lEPSR7QVpVUkVfUkVTT1VSQ0VfQVBQX0lEfScKICAgICAgLSAnT0lEQ19DTElFTlRfSUQ9JHtPSURDX0NMSUVOVF9JRH0nCiAgICAgIC0gJ09JRENfQ0xJRU5UX1NFQ1JFVD0ke09JRENfQ0xJRU5UX1NFQ1JFVH0nCiAgICAgIC0gJ09JRENfQVVUSF9VUkk9JHtPSURDX0FVVEhfVVJJfScKICAgICAgLSAnT0lEQ19UT0tFTl9VUkk9JHtPSURDX1RPS0VOX1VSSX0nCiAgICAgIC0gJ09JRENfVVNFUklORk9fVVJJPSR7T0lEQ19VU0VSSU5GT19VUkl9JwogICAgICAtICdPSURDX0xPR09VVF9VUkk9JHtPSURDX0xPR09VVF9VUkl9JwogICAgICAtICdPSURDX1VTRVJOQU1FX0NMQUlNPSR7T0lEQ19VU0VSTkFNRV9DTEFJTX0nCiAgICAgIC0gJ09JRENfRElTUExBWV9OQU1FPSR7T0lEQ19ESVNQTEFZX05BTUV9JwogICAgICAtICdPSURDX1NDT1BFUz0ke09JRENfU0NPUEVTfScKICAgICAgLSAnR0lUSFVCX0NMSUVOVF9JRD0ke0dJVEhVQl9DTElFTlRfSUR9JwogICAgICAtICdHSVRIVUJfQ0xJRU5UX1NFQ1JFVD0ke0dJVEhVQl9DTElFTlRfU0VDUkVUfScKICAgICAgLSAnR0lUSFVCX0FQUF9OQU1FPSR7R0lUSFVCX0FQUF9OQU1FfScKICAgICAgLSAnR0lUSFVCX0FQUF9JRD0ke0dJVEhVQl9BUFBfSUR9JwogICAgICAtICdHSVRIVUJfQVBQX1BSSVZBVEVfS0VZPSR7R0lUSFVCX0FQUF9QUklWQVRFX0tFWX0nCiAgICAgIC0gJ0RJU0NPUkRfQ0xJRU5UX0lEPSR7RElTQ09SRF9DTElFTlRfSUR9JwogICAgICAtICdESVNDT1JEX0NMSUVOVF9TRUNSRVQ9JHtESVNDT1JEX0NMSUVOVF9TRUNSRVR9JwogICAgICAtICdESVNDT1JEX1NFUlZFUl9JRD0ke0RJU0NPUkRfU0VSVkVSX0lEfScKICAgICAgLSAnRElTQ09SRF9TRVJWRVJfUk9MRVM9JHtESVNDT1JEX1NFUlZFUl9ST0xFU30nCiAgICAgIC0gJ1BHU1NMTU9ERT0ke1BHU1NMTU9ERTotZGlzYWJsZX0nCiAgICAgIC0gJ0ZPUkNFX0hUVFBTPSR7Rk9SQ0VfSFRUUFM6LXRydWV9JwogICAgICAtICdTTVRQX0hPU1Q9JHtTTVRQX0hPU1R9JwogICAgICAtICdTTVRQX1BPUlQ9JHtTTVRQX1BPUlR9JwogICAgICAtICdTTVRQX1VTRVJOQU1FPSR7U01UUF9VU0VSTkFNRX0nCiAgICAgIC0gJ1NNVFBfUEFTU1dPUkQ9JHtTTVRQX1BBU1NXT1JEfScKICAgICAgLSAnU01UUF9GUk9NX0VNQUlMPSR7U01UUF9GUk9NX0VNQUlMfScKICAgICAgLSAnU01UUF9SRVBMWV9FTUFJTD0ke1NNVFBfUkVQTFlfRU1BSUx9JwogICAgICAtICdTTVRQX1RMU19DSVBIRVJTPSR7U01UUF9UTFNfQ0lQSEVSU30nCiAgICAgIC0gJ1NNVFBfU0VDVVJFPSR7U01UUF9TRUNVUkV9JwogICAgICAtICdTTVRQX05BTUU9JHtTTVRQX05BTUV9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIGRpc2FibGU6IHRydWUKICByZWRpczoKICAgIGltYWdlOiAncmVkaXM6YWxwaW5lJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1JFRElTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF82NF9SRURJU30nCiAgICBjb21tYW5kOgogICAgICAtIHJlZGlzLXNlcnZlcgogICAgICAtICctLXJlcXVpcmVwYXNzJwogICAgICAtICcke1NFUlZJQ0VfUEFTU1dPUkRfNjRfUkVESVN9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHJlZGlzLWNsaQogICAgICAgIC0gJy1hJwogICAgICAgIC0gJyR7U0VSVklDRV9QQVNTV09SRF82NF9SRURJU30nCiAgICAgICAgLSBQSU5HCiAgICAgIGludGVydmFsOiAxMHMKICAgICAgdGltZW91dDogMzBzCiAgICAgIHJldHJpZXM6IDMKICBwb3N0Z3JlczoKICAgIGltYWdlOiAncG9zdGdyZXM6MTItYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAnZGF0YWJhc2UtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF82NF9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREFUQUJBU0U6LW91dGxpbmV9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHBnX2lzcmVhZHkKICAgICAgICAtICctVScKICAgICAgICAtICcke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30nCiAgICAgICAgLSAnLWQnCiAgICAgICAgLSAnJHtQT1NUR1JFU19EQVRBQkFTRTotb3V0bGluZX0nCiAgICAgIGludGVydmFsOiAzMHMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDMK", + "compose": "c2VydmljZXM6CiAgb3V0bGluZToKICAgIGltYWdlOiAnZG9ja2VyLmdldG91dGxpbmUuY29tL291dGxpbmV3aWtpL291dGxpbmU6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnc3RvcmFnZS1kYXRhOi92YXIvbGliL291dGxpbmUvZGF0YScKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHJlZGlzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX1VSTF9PVVRMSU5FXzMwMDAKICAgICAgLSBOT0RFX0VOVj1wcm9kdWN0aW9uCiAgICAgIC0gJ1NFQ1JFVF9LRVk9JHtTRVJWSUNFX0hFWF82NF9PVVRMSU5FfScKICAgICAgLSAnVVRJTFNfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF82NF9PVVRMSU5FfScKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vJHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9OiR7U0VSVklDRV9QQVNTV09SRF82NF9QT1NUR1JFU31AcG9zdGdyZXM6NTQzMi8ke1BPU1RHUkVTX0RBVEFCQVNFOi1vdXRsaW5lfScKICAgICAgLSAnUkVESVNfVVJMPXJlZGlzOi8vOiR7U0VSVklDRV9QQVNTV09SRF82NF9SRURJU31AcmVkaXM6NjM3OScKICAgICAgLSAnVVJMPSR7U0VSVklDRV9VUkxfT1VUTElORX0nCiAgICAgIC0gJ1BPUlQ9JHtPVVRMSU5FX1BPUlQ6LTMwMDB9JwogICAgICAtICdGSUxFX1NUT1JBR0U9JHtGSUxFX1NUT1JBR0U6LWxvY2FsfScKICAgICAgLSAnRklMRV9TVE9SQUdFX0xPQ0FMX1JPT1RfRElSPSR7RklMRV9TVE9SQUdFX0xPQ0FMX1JPT1RfRElSOi0vdmFyL2xpYi9vdXRsaW5lL2RhdGF9JwogICAgICAtICdGSUxFX1NUT1JBR0VfVVBMT0FEX01BWF9TSVpFPSR7RklMRV9TVE9SQUdFX1VQTE9BRF9NQVhfU0laRTotMjAwMH0nCiAgICAgIC0gJ0ZJTEVfU1RPUkFHRV9JTVBPUlRfTUFYX1NJWkU9JHtGSUxFX1NUT1JBR0VfSU1QT1JUX01BWF9TSVpFOi0xMDB9JwogICAgICAtICdGSUxFX1NUT1JBR0VfV09SS1NQQUNFX0lNUE9SVF9NQVhfU0laRT0ke0ZJTEVfU1RPUkFHRV9XT1JLU1BBQ0VfSU1QT1JUX01BWF9TSVpFfScKICAgICAgLSAnQVdTX0FDQ0VTU19LRVlfSUQ9JHtBV1NfQUNDRVNTX0tFWV9JRH0nCiAgICAgIC0gJ0FXU19TRUNSRVRfQUNDRVNTX0tFWT0ke0FXU19TRUNSRVRfQUNDRVNTX0tFWX0nCiAgICAgIC0gJ0FXU19SRUdJT049JHtBV1NfUkVHSU9OfScKICAgICAgLSAnQVdTX1MzX0FDQ0VMRVJBVEVfVVJMPSR7QVdTX1MzX0FDQ0VMRVJBVEVfVVJMfScKICAgICAgLSAnQVdTX1MzX1VQTE9BRF9CVUNLRVRfVVJMPSR7QVdTX1MzX1VQTE9BRF9CVUNLRVRfVVJMfScKICAgICAgLSAnQVdTX1MzX1VQTE9BRF9CVUNLRVRfTkFNRT0ke0FXU19TM19VUExPQURfQlVDS0VUX05BTUV9JwogICAgICAtICdBV1NfUzNfRk9SQ0VfUEFUSF9TVFlMRT0ke0FXU19TM19GT1JDRV9QQVRIX1NUWUxFOi10cnVlfScKICAgICAgLSAnQVdTX1MzX0FDTD0ke0FXU19TM19BQ0w6LXByaXZhdGV9JwogICAgICAtICdTTEFDS19DTElFTlRfSUQ9JHtTTEFDS19DTElFTlRfSUR9JwogICAgICAtICdTTEFDS19DTElFTlRfU0VDUkVUPSR7U0xBQ0tfQ0xJRU5UX1NFQ1JFVH0nCiAgICAgIC0gJ0dPT0dMRV9DTElFTlRfSUQ9JHtHT09HTEVfQ0xJRU5UX0lEfScKICAgICAgLSAnR09PR0xFX0NMSUVOVF9TRUNSRVQ9JHtHT09HTEVfQ0xJRU5UX1NFQ1JFVH0nCiAgICAgIC0gJ0FaVVJFX0NMSUVOVF9JRD0ke0FaVVJFX0NMSUVOVF9JRH0nCiAgICAgIC0gJ0FaVVJFX0NMSUVOVF9TRUNSRVQ9JHtBWlVSRV9DTElFTlRfU0VDUkVUfScKICAgICAgLSAnQVpVUkVfUkVTT1VSQ0VfQVBQX0lEPSR7QVpVUkVfUkVTT1VSQ0VfQVBQX0lEfScKICAgICAgLSAnT0lEQ19DTElFTlRfSUQ9JHtPSURDX0NMSUVOVF9JRH0nCiAgICAgIC0gJ09JRENfQ0xJRU5UX1NFQ1JFVD0ke09JRENfQ0xJRU5UX1NFQ1JFVH0nCiAgICAgIC0gJ09JRENfQVVUSF9VUkk9JHtPSURDX0FVVEhfVVJJfScKICAgICAgLSAnT0lEQ19UT0tFTl9VUkk9JHtPSURDX1RPS0VOX1VSSX0nCiAgICAgIC0gJ09JRENfVVNFUklORk9fVVJJPSR7T0lEQ19VU0VSSU5GT19VUkl9JwogICAgICAtICdPSURDX0xPR09VVF9VUkk9JHtPSURDX0xPR09VVF9VUkl9JwogICAgICAtICdPSURDX1VTRVJOQU1FX0NMQUlNPSR7T0lEQ19VU0VSTkFNRV9DTEFJTX0nCiAgICAgIC0gJ09JRENfRElTUExBWV9OQU1FPSR7T0lEQ19ESVNQTEFZX05BTUV9JwogICAgICAtICdPSURDX1NDT1BFUz0ke09JRENfU0NPUEVTfScKICAgICAgLSAnR0lUSFVCX0NMSUVOVF9JRD0ke0dJVEhVQl9DTElFTlRfSUR9JwogICAgICAtICdHSVRIVUJfQ0xJRU5UX1NFQ1JFVD0ke0dJVEhVQl9DTElFTlRfU0VDUkVUfScKICAgICAgLSAnR0lUSFVCX0FQUF9OQU1FPSR7R0lUSFVCX0FQUF9OQU1FfScKICAgICAgLSAnR0lUSFVCX0FQUF9JRD0ke0dJVEhVQl9BUFBfSUR9JwogICAgICAtICdHSVRIVUJfQVBQX1BSSVZBVEVfS0VZPSR7R0lUSFVCX0FQUF9QUklWQVRFX0tFWX0nCiAgICAgIC0gJ0RJU0NPUkRfQ0xJRU5UX0lEPSR7RElTQ09SRF9DTElFTlRfSUR9JwogICAgICAtICdESVNDT1JEX0NMSUVOVF9TRUNSRVQ9JHtESVNDT1JEX0NMSUVOVF9TRUNSRVR9JwogICAgICAtICdESVNDT1JEX1NFUlZFUl9JRD0ke0RJU0NPUkRfU0VSVkVSX0lEfScKICAgICAgLSAnRElTQ09SRF9TRVJWRVJfUk9MRVM9JHtESVNDT1JEX1NFUlZFUl9ST0xFU30nCiAgICAgIC0gJ1BHU1NMTU9ERT0ke1BHU1NMTU9ERTotZGlzYWJsZX0nCiAgICAgIC0gJ0ZPUkNFX0hUVFBTPSR7Rk9SQ0VfSFRUUFM6LXRydWV9JwogICAgICAtICdTTVRQX0hPU1Q9JHtTTVRQX0hPU1R9JwogICAgICAtICdTTVRQX1BPUlQ9JHtTTVRQX1BPUlR9JwogICAgICAtICdTTVRQX1VTRVJOQU1FPSR7U01UUF9VU0VSTkFNRX0nCiAgICAgIC0gJ1NNVFBfUEFTU1dPUkQ9JHtTTVRQX1BBU1NXT1JEfScKICAgICAgLSAnU01UUF9GUk9NX0VNQUlMPSR7U01UUF9GUk9NX0VNQUlMfScKICAgICAgLSAnU01UUF9SRVBMWV9FTUFJTD0ke1NNVFBfUkVQTFlfRU1BSUx9JwogICAgICAtICdTTVRQX1RMU19DSVBIRVJTPSR7U01UUF9UTFNfQ0lQSEVSU30nCiAgICAgIC0gJ1NNVFBfU0VDVVJFPSR7U01UUF9TRUNVUkV9JwogICAgICAtICdTTVRQX05BTUU9JHtTTVRQX05BTUV9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIGRpc2FibGU6IHRydWUKICByZWRpczoKICAgIGltYWdlOiAncmVkaXM6YWxwaW5lJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1JFRElTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF82NF9SRURJU30nCiAgICBjb21tYW5kOgogICAgICAtIHJlZGlzLXNlcnZlcgogICAgICAtICctLXJlcXVpcmVwYXNzJwogICAgICAtICcke1NFUlZJQ0VfUEFTU1dPUkRfNjRfUkVESVN9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHJlZGlzLWNsaQogICAgICAgIC0gJy1hJwogICAgICAgIC0gJyR7U0VSVklDRV9QQVNTV09SRF82NF9SRURJU30nCiAgICAgICAgLSBQSU5HCiAgICAgIGludGVydmFsOiAxMHMKICAgICAgdGltZW91dDogMzBzCiAgICAgIHJldHJpZXM6IDMKICBwb3N0Z3JlczoKICAgIGltYWdlOiAncG9zdGdyZXM6MTItYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAnZGF0YWJhc2UtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF82NF9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREFUQUJBU0U6LW91dGxpbmV9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHBnX2lzcmVhZHkKICAgICAgICAtICctVScKICAgICAgICAtICcke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30nCiAgICAgICAgLSAnLWQnCiAgICAgICAgLSAnJHtQT1NUR1JFU19EQVRBQkFTRTotb3V0bGluZX0nCiAgICAgIGludGVydmFsOiAzMHMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDMK", "tags": [ "knowledge base", "documentation" @@ -2000,7 +2000,7 @@ "homarr": { "documentation": "https://homarr.dev?utm_source=coolify.io", "slogan": "Homarr is a self-hosted homepage for your services.", - "compose": "c2VydmljZXM6CiAgaG9tYXJyOgogICAgaW1hZ2U6ICdnaGNyLmlvL2hvbWFyci1sYWJzL2hvbWFycjp2MS40MC4wJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9VUkxfSE9NQVJSXzc1NzUKICAgICAgLSBTRVJWSUNFX0hFWF8zMl9IT01BUlIKICAgICAgLSAnU0VDUkVUX0VOQ1JZUFRJT05fS0VZPSR7U0VSVklDRV9IRVhfMzJfSE9NQVJSfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJy92YXIvcnVuL2RvY2tlci5zb2NrOi92YXIvcnVuL2RvY2tlci5zb2NrJwogICAgICAtICcuL2hvbWFyci9hcHBkYXRhOi9hcHBkYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHdnZXQKICAgICAgICAtICctcScKICAgICAgICAtICctLXNwaWRlcicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjc1NzUnCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK", + "compose": "c2VydmljZXM6CiAgaG9tYXJyOgogICAgaW1hZ2U6ICdnaGNyLmlvL2hvbWFyci1sYWJzL2hvbWFycjp2MS40MC4wJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9VUkxfSE9NQVJSXzc1NzUKICAgICAgLSAnU0VDUkVUX0VOQ1JZUFRJT05fS0VZPSR7U0VSVklDRV9IRVhfNjRfSE9NQVJSfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJy92YXIvcnVuL2RvY2tlci5zb2NrOi92YXIvcnVuL2RvY2tlci5zb2NrJwogICAgICAtICcuL2hvbWFyci9hcHBkYXRhOi9hcHBkYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHdnZXQKICAgICAgICAtICctcScKICAgICAgICAtICctLXNwaWRlcicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjc1NzUnCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK", "tags": [ "homarr", "self-hosted", @@ -3408,7 +3408,7 @@ "open-archiver": { "documentation": "https://docs.openarchiver.com/?utm_source=coolify.io", "slogan": "A self-hosted, open-source email archiving solution with full-text search capability.", - "compose": "c2VydmljZXM6CiAgb3Blbi1hcmNoaXZlcjoKICAgIGltYWdlOiAnbG9naWNsYWJzaHEvb3Blbi1hcmNoaXZlcjpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX1VSTF9PUEVOQVJDSElWRVJfMzAwMAogICAgICAtICdFTkNSWVBUSU9OX0tFWT0ke1NFUlZJQ0VfSEVYXzMyX0VOQ1JZUFRJT05LRVl9JwogICAgICAtICdTVE9SQUdFX0VOQ1JZUFRJT05fS0VZPSR7U0VSVklDRV9IRVhfMzJfU1RPUkFHRUVOQ1JZUFRJT05LRVl9JwogICAgICAtICdQT1JUX0JBQ0tFTkQ9JHtQT1JUX0JBQ0tFTkQ6LTQwMDB9JwogICAgICAtICdQT1JUX0ZST05URU5EPSR7UE9SVF9GUk9OVEVORDotMzAwMH0nCiAgICAgIC0gJ05PREVfRU5WPSR7Tk9ERV9FTlY6LXByb2R1Y3Rpb259JwogICAgICAtICdTWU5DX0ZSRVFVRU5DWT0ke1NZTkNfRlJFUVVFTkNZOi0qICogKiAqICp9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi1vcGVuX2FyY2hpdmV9JwogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfScKICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfScKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzcWw6Ly8ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU306JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfUBwb3N0Z3Jlczo1NDMyLyR7UE9TVEdSRVNfREI6LW9wZW4tYXJjaGl2ZXItZGJ9JwogICAgICAtICdNRUlMSV9NQVNURVJfS0VZPSR7U0VSVklDRV9QQVNTV09SRF9NRUlMSVNFQVJDSH0nCiAgICAgIC0gJ01FSUxJX0hPU1Q9aHR0cDovL21laWxpc2VhcmNoOjc3MDAnCiAgICAgIC0gUkVESVNfSE9TVD12YWxrZXkKICAgICAgLSBSRURJU19QT1JUPTYzNzkKICAgICAgLSAnUkVESVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1ZBTEtFWX0nCiAgICAgIC0gUkVESVNfVExTX0VOQUJMRUQ9ZmFsc2UKICAgICAgLSAnU1RPUkFHRV9UWVBFPSR7U1RPUkFHRV9UWVBFOi1sb2NhbH0nCiAgICAgIC0gJ1NUT1JBR0VfTE9DQUxfUk9PVF9QQVRIPSR7U1RPUkFHRV9MT0NBTF9ST09UX1BBVEg6LS92YXIvZGF0YS9vcGVuLWFyY2hpdmVyfScKICAgICAgLSAnQk9EWV9TSVpFX0xJTUlUPSR7Qk9EWV9TSVpFX0xJTUlUOi0xMDBNfScKICAgICAgLSAnU1RPUkFHRV9TM19FTkRQT0lOVD0ke1NUT1JBR0VfUzNfRU5EUE9JTlR9JwogICAgICAtICdTVE9SQUdFX1MzX0JVQ0tFVD0ke1NUT1JBR0VfUzNfQlVDS0VUfScKICAgICAgLSAnU1RPUkFHRV9TM19BQ0NFU1NfS0VZX0lEPSR7U1RPUkFHRV9TM19BQ0NFU1NfS0VZX0lEfScKICAgICAgLSAnU1RPUkFHRV9TM19TRUNSRVRfQUNDRVNTX0tFWT0ke1NUT1JBR0VfUzNfU0VDUkVUX0FDQ0VTU19LRVl9JwogICAgICAtICdTVE9SQUdFX1MzX1JFR0lPTj0ke1NUT1JBR0VfUzNfUkVHSU9OfScKICAgICAgLSAnU1RPUkFHRV9TM19GT1JDRV9QQVRIX1NUWUxFPSR7U1RPUkFHRV9TM19GT1JDRV9QQVRIX1NUWUxFOi1mYWxzZX0nCiAgICAgIC0gJ0pXVF9TRUNSRVQ9JHtTRVJWSUNFX0JBU0U2NF8xMjhfSldUfScKICAgICAgLSAnSldUX0VYUElSRVNfSU49JHtKV1RfRVhQSVJFU19JTjotN2R9JwogICAgICAtICdSQVRFX0xJTUlUX1dJTkRPV19NUz0ke1JBVEVfTElNSVRfV0lORE9XX01TOi02MDAwMH0nCiAgICAgIC0gJ1JBVEVfTElNSVRfTUFYX1JFUVVFU1RTPSR7UkFURV9MSU1JVF9NQVhfUkVRVUVTVFM6LTEwMH0nCiAgICB2b2x1bWVzOgogICAgICAtICdhcmNoaXZlci1kYXRhOi92YXIvZGF0YS9vcGVuLWFyY2hpdmVyJwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgdmFsa2V5OgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIG1laWxpc2VhcmNoOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE3LWFscGluZScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi1vcGVuLWFyY2hpdmVyLWRifScKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIC0gTENfQUxMPUMKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3Bvc3RncmVzLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDEwcwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICB2YWxrZXk6CiAgICBpbWFnZTogJ3ZhbGtleS92YWxrZXk6OC1hbHBpbmUnCiAgICBjb21tYW5kOiAndmFsa2V5LXNlcnZlciAtLXJlcXVpcmVwYXNzICR7U0VSVklDRV9QQVNTV09SRF9WQUxLRVl9JwogICAgdm9sdW1lczoKICAgICAgLSAndmFsa2V5LWRhdGE6L2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSBwaW5nCiAgICAgIGludGVydmFsOiAxMHMKICAgICAgdGltZW91dDogNXMKICAgICAgcmV0cmllczogMwogIG1laWxpc2VhcmNoOgogICAgaW1hZ2U6ICdnZXRtZWlsaS9tZWlsaXNlYXJjaDp2MS4xNScKICAgIGVudmlyb25tZW50OgogICAgICAtICdNRUlMSV9NQVNURVJfS0VZPSR7U0VSVklDRV9QQVNTV09SRF9NRUlMSVNFQVJDSH0nCiAgICB2b2x1bWVzOgogICAgICAtICdtZWlsaXNlYXJjaC1kYXRhOi9tZWlsaV9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjc3MDAvaGVhbHRoJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==", + "compose": "c2VydmljZXM6CiAgb3Blbi1hcmNoaXZlcjoKICAgIGltYWdlOiAnbG9naWNsYWJzaHEvb3Blbi1hcmNoaXZlcjpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX1VSTF9PUEVOQVJDSElWRVJfMzAwMAogICAgICAtICdFTkNSWVBUSU9OX0tFWT0ke1NFUlZJQ0VfSEVYXzY0X0VOQ1JZUFRJT05LRVl9JwogICAgICAtICdTVE9SQUdFX0VOQ1JZUFRJT05fS0VZPSR7U0VSVklDRV9IRVhfNjRfU1RPUkFHRUVOQ1JZUFRJT05LRVl9JwogICAgICAtICdQT1JUX0JBQ0tFTkQ9JHtQT1JUX0JBQ0tFTkQ6LTQwMDB9JwogICAgICAtICdQT1JUX0ZST05URU5EPSR7UE9SVF9GUk9OVEVORDotMzAwMH0nCiAgICAgIC0gJ05PREVfRU5WPSR7Tk9ERV9FTlY6LXByb2R1Y3Rpb259JwogICAgICAtICdTWU5DX0ZSRVFVRU5DWT0ke1NZTkNfRlJFUVVFTkNZOi0qICogKiAqICp9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi1vcGVuX2FyY2hpdmV9JwogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfScKICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfScKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzcWw6Ly8ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU306JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfUBwb3N0Z3Jlczo1NDMyLyR7UE9TVEdSRVNfREI6LW9wZW4tYXJjaGl2ZXItZGJ9JwogICAgICAtICdNRUlMSV9NQVNURVJfS0VZPSR7U0VSVklDRV9QQVNTV09SRF9NRUlMSVNFQVJDSH0nCiAgICAgIC0gJ01FSUxJX0hPU1Q9aHR0cDovL21laWxpc2VhcmNoOjc3MDAnCiAgICAgIC0gUkVESVNfSE9TVD12YWxrZXkKICAgICAgLSBSRURJU19QT1JUPTYzNzkKICAgICAgLSAnUkVESVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1ZBTEtFWX0nCiAgICAgIC0gUkVESVNfVExTX0VOQUJMRUQ9ZmFsc2UKICAgICAgLSAnU1RPUkFHRV9UWVBFPSR7U1RPUkFHRV9UWVBFOi1sb2NhbH0nCiAgICAgIC0gJ1NUT1JBR0VfTE9DQUxfUk9PVF9QQVRIPSR7U1RPUkFHRV9MT0NBTF9ST09UX1BBVEg6LS92YXIvZGF0YS9vcGVuLWFyY2hpdmVyfScKICAgICAgLSAnQk9EWV9TSVpFX0xJTUlUPSR7Qk9EWV9TSVpFX0xJTUlUOi0xMDBNfScKICAgICAgLSAnU1RPUkFHRV9TM19FTkRQT0lOVD0ke1NUT1JBR0VfUzNfRU5EUE9JTlR9JwogICAgICAtICdTVE9SQUdFX1MzX0JVQ0tFVD0ke1NUT1JBR0VfUzNfQlVDS0VUfScKICAgICAgLSAnU1RPUkFHRV9TM19BQ0NFU1NfS0VZX0lEPSR7U1RPUkFHRV9TM19BQ0NFU1NfS0VZX0lEfScKICAgICAgLSAnU1RPUkFHRV9TM19TRUNSRVRfQUNDRVNTX0tFWT0ke1NUT1JBR0VfUzNfU0VDUkVUX0FDQ0VTU19LRVl9JwogICAgICAtICdTVE9SQUdFX1MzX1JFR0lPTj0ke1NUT1JBR0VfUzNfUkVHSU9OfScKICAgICAgLSAnU1RPUkFHRV9TM19GT1JDRV9QQVRIX1NUWUxFPSR7U1RPUkFHRV9TM19GT1JDRV9QQVRIX1NUWUxFOi1mYWxzZX0nCiAgICAgIC0gJ0pXVF9TRUNSRVQ9JHtTRVJWSUNFX0JBU0U2NF8xMjhfSldUfScKICAgICAgLSAnSldUX0VYUElSRVNfSU49JHtKV1RfRVhQSVJFU19JTjotN2R9JwogICAgICAtICdSQVRFX0xJTUlUX1dJTkRPV19NUz0ke1JBVEVfTElNSVRfV0lORE9XX01TOi02MDAwMH0nCiAgICAgIC0gJ1JBVEVfTElNSVRfTUFYX1JFUVVFU1RTPSR7UkFURV9MSU1JVF9NQVhfUkVRVUVTVFM6LTEwMH0nCiAgICB2b2x1bWVzOgogICAgICAtICdhcmNoaXZlci1kYXRhOi92YXIvZGF0YS9vcGVuLWFyY2hpdmVyJwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgdmFsa2V5OgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIG1laWxpc2VhcmNoOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE3LWFscGluZScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi1vcGVuLWFyY2hpdmVyLWRifScKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIC0gTENfQUxMPUMKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3Bvc3RncmVzLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDEwcwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICB2YWxrZXk6CiAgICBpbWFnZTogJ3ZhbGtleS92YWxrZXk6OC1hbHBpbmUnCiAgICBjb21tYW5kOiAndmFsa2V5LXNlcnZlciAtLXJlcXVpcmVwYXNzICR7U0VSVklDRV9QQVNTV09SRF9WQUxLRVl9JwogICAgdm9sdW1lczoKICAgICAgLSAndmFsa2V5LWRhdGE6L2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSBwaW5nCiAgICAgIGludGVydmFsOiAxMHMKICAgICAgdGltZW91dDogNXMKICAgICAgcmV0cmllczogMwogIG1laWxpc2VhcmNoOgogICAgaW1hZ2U6ICdnZXRtZWlsaS9tZWlsaXNlYXJjaDp2MS4xNScKICAgIGVudmlyb25tZW50OgogICAgICAtICdNRUlMSV9NQVNURVJfS0VZPSR7U0VSVklDRV9QQVNTV09SRF9NRUlMSVNFQVJDSH0nCiAgICB2b2x1bWVzOgogICAgICAtICdtZWlsaXNlYXJjaC1kYXRhOi9tZWlsaV9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjc3MDAvaGVhbHRoJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==", "tags": [ "email archiving", "email", diff --git a/templates/service-templates.json b/templates/service-templates.json index cc909dc68..ea9fd145a 100644 --- a/templates/service-templates.json +++ b/templates/service-templates.json @@ -299,7 +299,7 @@ "bluesky-pds": { "documentation": "https://github.com/bluesky-social/pds?utm_source=coolify.io", "slogan": "Bluesky PDS (Personal Data Server)", - "compose": "c2VydmljZXM6CiAgcGRzOgogICAgaW1hZ2U6ICdnaGNyLmlvL2JsdWVza3ktc29jaWFsL3BkczowLjQuMTgyJwogICAgdm9sdW1lczoKICAgICAgLSAncGRzLWRhdGE6L3BkcycKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9QRFNfMzAwMAogICAgICAtICdQRFNfSE9TVE5BTUU9JHtTRVJWSUNFX0ZRRE5fUERTfScKICAgICAgLSAnUERTX0pXVF9TRUNSRVQ9JHtTRVJWSUNFX0hFWF8zMl9KV1RTRUNSRVR9JwogICAgICAtICdQRFNfQURNSU5fUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX0FETUlOfScKICAgICAgLSAnUERTX0FETUlOX0VNQUlMPSR7UERTX0FETUlOX0VNQUlMfScKICAgICAgLSAnUERTX1BMQ19ST1RBVElPTl9LRVlfSzI1Nl9QUklWQVRFX0tFWV9IRVg9JHtTRVJWSUNFX0hFWF8zMl9ST1RBVElPTktFWX0nCiAgICAgIC0gJ1BEU19EQVRBX0RJUkVDVE9SWT0ke1BEU19EQVRBX0RJUkVDVE9SWTotL3Bkc30nCiAgICAgIC0gJ1BEU19CTE9CU1RPUkVfRElTS19MT0NBVElPTj0ke1BEU19EQVRBX0RJUkVDVE9SWTotL3Bkc30vYmxvY2tzJwogICAgICAtICdQRFNfQkxPQl9VUExPQURfTElNSVQ9JHtQRFNfQkxPQl9VUExPQURfTElNSVQ6LTEwNDg1NzYwMH0nCiAgICAgIC0gJ1BEU19ESURfUExDX1VSTD0ke1BEU19ESURfUExDX1VSTDotaHR0cHM6Ly9wbGMuZGlyZWN0b3J5fScKICAgICAgLSAnUERTX0VNQUlMX0ZST01fQUREUkVTUz0ke1BEU19FTUFJTF9GUk9NX0FERFJFU1N9JwogICAgICAtICdQRFNfRU1BSUxfU01UUF9VUkw9JHtQRFNfRU1BSUxfU01UUF9VUkx9JwogICAgICAtICdQRFNfQlNLWV9BUFBfVklFV19VUkw9JHtQRFNfQlNLWV9BUFBfVklFV19VUkw6LWh0dHBzOi8vYXBpLmJza3kuYXBwfScKICAgICAgLSAnUERTX0JTS1lfQVBQX1ZJRVdfRElEPSR7UERTX0JTS1lfQVBQX1ZJRVdfRElEOi1kaWQ6d2ViOmFwaS5ic2t5LmFwcH0nCiAgICAgIC0gJ1BEU19SRVBPUlRfU0VSVklDRV9GUUROPSR7UERTX1JFUE9SVF9TRVJWSUNFX0ZRRE46LWh0dHBzOi8vbW9kLmJza3kuYXBwL3hycGMvY29tLmF0cHJvdG8ubW9kZXJhdGlvbi5jcmVhdGVSZXBvcnR9JwogICAgICAtICdQRFNfUkVQT1JUX1NFUlZJQ0VfRElEPSR7UERTX1JFUE9SVF9TRVJWSUNFX0RJRDotZGlkOnBsYzphcjdjNGJ5NDZxamR5ZGhkZXZ2cm5kYWN9JwogICAgICAtICdQRFNfQ1JBV0xFUlM9JHtQRFNfQ1JBV0xFUlM6LWh0dHBzOi8vYnNreS5uZXR3b3JrfScKICAgICAgLSAnTE9HX0VOQUJMRUQ9JHtMT0dfRU5BQkxFRDotdHJ1ZX0nCiAgICBjb21tYW5kOiAic2ggLWMgJ1xuICBzZXQgLWV1byBwaXBlZmFpbFxuICBlY2hvIFwiSW5zdGFsbGluZyByZXF1aXJlZCBwYWNrYWdlcyBhbmQgcGRzYWRtaW4uLi5cIlxuICBhcGsgYWRkIC0tbm8tY2FjaGUgb3BlbnNzbCBjdXJsIGJhc2gganEgY29yZXV0aWxzIGdudXBnIHV0aWwtbGludXgtbWlzYyA+L2Rldi9udWxsXG4gIGN1cmwgLW8gL3Vzci9sb2NhbC9iaW4vcGRzYWRtaW4uc2ggaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2JsdWVza3ktc29jaWFsL3Bkcy9tYWluL3Bkc2FkbWluLnNoXG4gIGNobW9kIDcwMCAvdXNyL2xvY2FsL2Jpbi9wZHNhZG1pbi5zaFxuICBsbiAtc2YgL3Vzci9sb2NhbC9iaW4vcGRzYWRtaW4uc2ggL3Vzci9sb2NhbC9iaW4vcGRzYWRtaW5cbiAgZWNobyBcIkNyZWF0aW5nIGFuIGVtcHR5IHBkcy5lbnYgZmlsZSBzbyBwZHNhZG1pbiB3b3Jrcy4uLlwiXG4gIHRvdWNoICR7UERTX0RBVEFfRElSRUNUT1JZfS9wZHMuZW52XG4gIGVjaG8gXCJMYXVuY2hpbmcgUERTLCBlbmpveSEuLi5cIlxuICBleGVjIG5vZGUgLS1lbmFibGUtc291cmNlLW1hcHMgaW5kZXguanNcbidcbiIKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSB3Z2V0CiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwL3hycGMvX2hlYWx0aCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxMAo=", + "compose": "c2VydmljZXM6CiAgcGRzOgogICAgaW1hZ2U6ICdnaGNyLmlvL2JsdWVza3ktc29jaWFsL3BkczowLjQuMTgyJwogICAgdm9sdW1lczoKICAgICAgLSAncGRzLWRhdGE6L3BkcycKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9QRFNfMzAwMAogICAgICAtICdQRFNfSE9TVE5BTUU9JHtTRVJWSUNFX0ZRRE5fUERTfScKICAgICAgLSAnUERTX0pXVF9TRUNSRVQ9JHtTRVJWSUNFX0hFWF82NF9KV1RTRUNSRVR9JwogICAgICAtICdQRFNfQURNSU5fUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX0FETUlOfScKICAgICAgLSAnUERTX0FETUlOX0VNQUlMPSR7UERTX0FETUlOX0VNQUlMfScKICAgICAgLSAnUERTX1BMQ19ST1RBVElPTl9LRVlfSzI1Nl9QUklWQVRFX0tFWV9IRVg9JHtTRVJWSUNFX0hFWF82NF9ST1RBVElPTktFWX0nCiAgICAgIC0gJ1BEU19EQVRBX0RJUkVDVE9SWT0ke1BEU19EQVRBX0RJUkVDVE9SWTotL3Bkc30nCiAgICAgIC0gJ1BEU19CTE9CU1RPUkVfRElTS19MT0NBVElPTj0ke1BEU19EQVRBX0RJUkVDVE9SWTotL3Bkc30vYmxvY2tzJwogICAgICAtICdQRFNfQkxPQl9VUExPQURfTElNSVQ9JHtQRFNfQkxPQl9VUExPQURfTElNSVQ6LTEwNDg1NzYwMH0nCiAgICAgIC0gJ1BEU19ESURfUExDX1VSTD0ke1BEU19ESURfUExDX1VSTDotaHR0cHM6Ly9wbGMuZGlyZWN0b3J5fScKICAgICAgLSAnUERTX0VNQUlMX0ZST01fQUREUkVTUz0ke1BEU19FTUFJTF9GUk9NX0FERFJFU1N9JwogICAgICAtICdQRFNfRU1BSUxfU01UUF9VUkw9JHtQRFNfRU1BSUxfU01UUF9VUkx9JwogICAgICAtICdQRFNfQlNLWV9BUFBfVklFV19VUkw9JHtQRFNfQlNLWV9BUFBfVklFV19VUkw6LWh0dHBzOi8vYXBpLmJza3kuYXBwfScKICAgICAgLSAnUERTX0JTS1lfQVBQX1ZJRVdfRElEPSR7UERTX0JTS1lfQVBQX1ZJRVdfRElEOi1kaWQ6d2ViOmFwaS5ic2t5LmFwcH0nCiAgICAgIC0gJ1BEU19SRVBPUlRfU0VSVklDRV9GUUROPSR7UERTX1JFUE9SVF9TRVJWSUNFX0ZRRE46LWh0dHBzOi8vbW9kLmJza3kuYXBwL3hycGMvY29tLmF0cHJvdG8ubW9kZXJhdGlvbi5jcmVhdGVSZXBvcnR9JwogICAgICAtICdQRFNfUkVQT1JUX1NFUlZJQ0VfRElEPSR7UERTX1JFUE9SVF9TRVJWSUNFX0RJRDotZGlkOnBsYzphcjdjNGJ5NDZxamR5ZGhkZXZ2cm5kYWN9JwogICAgICAtICdQRFNfQ1JBV0xFUlM9JHtQRFNfQ1JBV0xFUlM6LWh0dHBzOi8vYnNreS5uZXR3b3JrfScKICAgICAgLSAnTE9HX0VOQUJMRUQ9JHtMT0dfRU5BQkxFRDotdHJ1ZX0nCiAgICBjb21tYW5kOiAic2ggLWMgJ1xuICBzZXQgLWV1byBwaXBlZmFpbFxuICBlY2hvIFwiSW5zdGFsbGluZyByZXF1aXJlZCBwYWNrYWdlcyBhbmQgcGRzYWRtaW4uLi5cIlxuICBhcGsgYWRkIC0tbm8tY2FjaGUgb3BlbnNzbCBjdXJsIGJhc2gganEgY29yZXV0aWxzIGdudXBnIHV0aWwtbGludXgtbWlzYyA+L2Rldi9udWxsXG4gIGN1cmwgLW8gL3Vzci9sb2NhbC9iaW4vcGRzYWRtaW4uc2ggaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2JsdWVza3ktc29jaWFsL3Bkcy9tYWluL3Bkc2FkbWluLnNoXG4gIGNobW9kIDcwMCAvdXNyL2xvY2FsL2Jpbi9wZHNhZG1pbi5zaFxuICBsbiAtc2YgL3Vzci9sb2NhbC9iaW4vcGRzYWRtaW4uc2ggL3Vzci9sb2NhbC9iaW4vcGRzYWRtaW5cbiAgZWNobyBcIkNyZWF0aW5nIGFuIGVtcHR5IHBkcy5lbnYgZmlsZSBzbyBwZHNhZG1pbiB3b3Jrcy4uLlwiXG4gIHRvdWNoICR7UERTX0RBVEFfRElSRUNUT1JZfS9wZHMuZW52XG4gIGVjaG8gXCJMYXVuY2hpbmcgUERTLCBlbmpveSEuLi5cIlxuICBleGVjIG5vZGUgLS1lbmFibGUtc291cmNlLW1hcHMgaW5kZXguanNcbidcbiIKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSB3Z2V0CiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwL3hycGMvX2hlYWx0aCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxMAo=", "tags": [ "bluesky", "pds", @@ -730,7 +730,7 @@ "convex": { "documentation": "https://github.com/get-convex/convex-backend/blob/main/self-hosted/README.md?utm_source=coolify.io", "slogan": "Convex is the open-source reactive database for app developers.", - "compose": "c2VydmljZXM6CiAgYmFja2VuZDoKICAgIGltYWdlOiAnZ2hjci5pby9nZXQtY29udmV4L2NvbnZleC1iYWNrZW5kOmE5YTc2MGNhMTAzOTllZDQyZTFiNGJiODdjNzg1MzlhMjM1NDg4YzcnCiAgICB2b2x1bWVzOgogICAgICAtICdkYXRhOi9jb252ZXgvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9CQUNLRU5EXzMyMTAKICAgICAgLSAnSU5TVEFOQ0VfTkFNRT0ke0lOU1RBTkNFX05BTUU6LXNlbGYtaG9zdGVkLWNvbnZleH0nCiAgICAgIC0gJ0lOU1RBTkNFX1NFQ1JFVD0ke1NFUlZJQ0VfSEVYXzMyX1NFQ1JFVH0nCiAgICAgIC0gJ0NPTlZFWF9SRUxFQVNFX1ZFUlNJT05fREVWPSR7Q09OVkVYX1JFTEVBU0VfVkVSU0lPTl9ERVY6LX0nCiAgICAgIC0gJ0FDVElPTlNfVVNFUl9USU1FT1VUX1NFQ1M9JHtBQ1RJT05TX1VTRVJfVElNRU9VVF9TRUNTOi19JwogICAgICAtICdDT05WRVhfQ0xPVURfT1JJR0lOPSR7U0VSVklDRV9GUUROX0RBU0hCT0FSRH0nCiAgICAgIC0gJ0NPTlZFWF9TSVRFX09SSUdJTj0ke1NFUlZJQ0VfRlFETl9CQUNLRU5EfScKICAgICAgLSAnREFUQUJBU0VfVVJMPSR7REFUQUJBU0VfVVJMOi19JwogICAgICAtICdESVNBQkxFX0JFQUNPTj0ke0RJU0FCTEVfQkVBQ09OOj9mYWxzZX0nCiAgICAgIC0gJ1JFREFDVF9MT0dTX1RPX0NMSUVOVD0ke1JFREFDVF9MT0dTX1RPX0NMSUVOVDo/ZmFsc2V9JwogICAgICAtICdET19OT1RfUkVRVUlSRV9TU0w9JHtET19OT1RfUkVRVUlSRV9TU0w6P3RydWV9JwogICAgICAtICdQT1NUR1JFU19VUkw9JHtQT1NUR1JFU19VUkw6LX0nCiAgICAgIC0gJ01ZU1FMX1VSTD0ke01ZU1FMX1VSTDotfScKICAgICAgLSAnUlVTVF9MT0c9JHtSVVNUX0xPRzotaW5mb30nCiAgICAgIC0gJ1JVU1RfQkFDS1RSQUNFPSR7UlVTVF9CQUNLVFJBQ0U6LX0nCiAgICAgIC0gJ0FXU19SRUdJT049JHtBV1NfUkVHSU9OOi19JwogICAgICAtICdBV1NfQUNDRVNTX0tFWV9JRD0ke0FXU19BQ0NFU1NfS0VZX0lEOi19JwogICAgICAtICdBV1NfU0VDUkVUX0FDQ0VTU19LRVk9JHtBV1NfU0VDUkVUX0FDQ0VTU19LRVk6LX0nCiAgICAgIC0gJ0FXU19TRVNTSU9OX1RPS0VOPSR7QVdTX1NFU1NJT05fVE9LRU46LX0nCiAgICAgIC0gJ0FXU19TM19GT1JDRV9QQVRIX1NUWUxFPSR7QVdTX1MzX0ZPUkNFX1BBVEhfU1RZTEU6LX0nCiAgICAgIC0gJ0FXU19TM19ESVNBQkxFX1NTRT0ke0FXU19TM19ESVNBQkxFX1NTRTotfScKICAgICAgLSAnQVdTX1MzX0RJU0FCTEVfQ0hFQ0tTVU1TPSR7QVdTX1MzX0RJU0FCTEVfQ0hFQ0tTVU1TOi19JwogICAgICAtICdTM19TVE9SQUdFX0VYUE9SVFNfQlVDS0VUPSR7UzNfU1RPUkFHRV9FWFBPUlRTX0JVQ0tFVDotfScKICAgICAgLSAnUzNfU1RPUkFHRV9TTkFQU0hPVF9JTVBPUlRTX0JVQ0tFVD0ke1MzX1NUT1JBR0VfU05BUFNIT1RfSU1QT1JUU19CVUNLRVQ6LX0nCiAgICAgIC0gJ1MzX1NUT1JBR0VfTU9EVUxFU19CVUNLRVQ9JHtTM19TVE9SQUdFX01PRFVMRVNfQlVDS0VUOi19JwogICAgICAtICdTM19TVE9SQUdFX0ZJTEVTX0JVQ0tFVD0ke1MzX1NUT1JBR0VfRklMRVNfQlVDS0VUOi19JwogICAgICAtICdTM19TVE9SQUdFX1NFQVJDSF9CVUNLRVQ9JHtTM19TVE9SQUdFX1NFQVJDSF9CVUNLRVQ6LX0nCiAgICAgIC0gJ1MzX0VORFBPSU5UX1VSTD0ke1MzX0VORFBPSU5UX1VSTDotfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiAnY3VybCAtZiBodHRwOi8vMTI3LjAuMC4xOjMyMTAvdmVyc2lvbicKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHN0YXJ0X3BlcmlvZDogMTBzCiAgZGFzaGJvYXJkOgogICAgaW1hZ2U6ICdnaGNyLmlvL2dldC1jb252ZXgvY29udmV4LWRhc2hib2FyZDphOWE3NjBjYTEwMzk5ZWQ0MmUxYjRiYjg3Yzc4NTM5YTIzNTQ4OGM3JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0RBU0hCT0FSRF82NzkxCiAgICAgIC0gJ05FWFRfUFVCTElDX0RFUExPWU1FTlRfVVJMPSR7U0VSVklDRV9GUUROX0JBQ0tFTkR9JwogICAgZGVwZW5kc19vbjoKICAgICAgYmFja2VuZDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6ICdjdXJsIC1mIGh0dHA6Ly8xMjcuMC4wLjE6Njc5MS8nCiAgICAgIGludGVydmFsOiA1cwogICAgICBzdGFydF9wZXJpb2Q6IDVzCg==", + "compose": "c2VydmljZXM6CiAgYmFja2VuZDoKICAgIGltYWdlOiAnZ2hjci5pby9nZXQtY29udmV4L2NvbnZleC1iYWNrZW5kOmE5YTc2MGNhMTAzOTllZDQyZTFiNGJiODdjNzg1MzlhMjM1NDg4YzcnCiAgICB2b2x1bWVzOgogICAgICAtICdkYXRhOi9jb252ZXgvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9CQUNLRU5EXzMyMTAKICAgICAgLSAnSU5TVEFOQ0VfTkFNRT0ke0lOU1RBTkNFX05BTUU6LXNlbGYtaG9zdGVkLWNvbnZleH0nCiAgICAgIC0gJ0lOU1RBTkNFX1NFQ1JFVD0ke1NFUlZJQ0VfSEVYXzY0X1NFQ1JFVH0nCiAgICAgIC0gJ0NPTlZFWF9SRUxFQVNFX1ZFUlNJT05fREVWPSR7Q09OVkVYX1JFTEVBU0VfVkVSU0lPTl9ERVY6LX0nCiAgICAgIC0gJ0FDVElPTlNfVVNFUl9USU1FT1VUX1NFQ1M9JHtBQ1RJT05TX1VTRVJfVElNRU9VVF9TRUNTOi19JwogICAgICAtICdDT05WRVhfQ0xPVURfT1JJR0lOPSR7U0VSVklDRV9GUUROX0RBU0hCT0FSRH0nCiAgICAgIC0gJ0NPTlZFWF9TSVRFX09SSUdJTj0ke1NFUlZJQ0VfRlFETl9CQUNLRU5EfScKICAgICAgLSAnREFUQUJBU0VfVVJMPSR7REFUQUJBU0VfVVJMOi19JwogICAgICAtICdESVNBQkxFX0JFQUNPTj0ke0RJU0FCTEVfQkVBQ09OOj9mYWxzZX0nCiAgICAgIC0gJ1JFREFDVF9MT0dTX1RPX0NMSUVOVD0ke1JFREFDVF9MT0dTX1RPX0NMSUVOVDo/ZmFsc2V9JwogICAgICAtICdET19OT1RfUkVRVUlSRV9TU0w9JHtET19OT1RfUkVRVUlSRV9TU0w6P3RydWV9JwogICAgICAtICdQT1NUR1JFU19VUkw9JHtQT1NUR1JFU19VUkw6LX0nCiAgICAgIC0gJ01ZU1FMX1VSTD0ke01ZU1FMX1VSTDotfScKICAgICAgLSAnUlVTVF9MT0c9JHtSVVNUX0xPRzotaW5mb30nCiAgICAgIC0gJ1JVU1RfQkFDS1RSQUNFPSR7UlVTVF9CQUNLVFJBQ0U6LX0nCiAgICAgIC0gJ0FXU19SRUdJT049JHtBV1NfUkVHSU9OOi19JwogICAgICAtICdBV1NfQUNDRVNTX0tFWV9JRD0ke0FXU19BQ0NFU1NfS0VZX0lEOi19JwogICAgICAtICdBV1NfU0VDUkVUX0FDQ0VTU19LRVk9JHtBV1NfU0VDUkVUX0FDQ0VTU19LRVk6LX0nCiAgICAgIC0gJ0FXU19TRVNTSU9OX1RPS0VOPSR7QVdTX1NFU1NJT05fVE9LRU46LX0nCiAgICAgIC0gJ0FXU19TM19GT1JDRV9QQVRIX1NUWUxFPSR7QVdTX1MzX0ZPUkNFX1BBVEhfU1RZTEU6LX0nCiAgICAgIC0gJ0FXU19TM19ESVNBQkxFX1NTRT0ke0FXU19TM19ESVNBQkxFX1NTRTotfScKICAgICAgLSAnQVdTX1MzX0RJU0FCTEVfQ0hFQ0tTVU1TPSR7QVdTX1MzX0RJU0FCTEVfQ0hFQ0tTVU1TOi19JwogICAgICAtICdTM19TVE9SQUdFX0VYUE9SVFNfQlVDS0VUPSR7UzNfU1RPUkFHRV9FWFBPUlRTX0JVQ0tFVDotfScKICAgICAgLSAnUzNfU1RPUkFHRV9TTkFQU0hPVF9JTVBPUlRTX0JVQ0tFVD0ke1MzX1NUT1JBR0VfU05BUFNIT1RfSU1QT1JUU19CVUNLRVQ6LX0nCiAgICAgIC0gJ1MzX1NUT1JBR0VfTU9EVUxFU19CVUNLRVQ9JHtTM19TVE9SQUdFX01PRFVMRVNfQlVDS0VUOi19JwogICAgICAtICdTM19TVE9SQUdFX0ZJTEVTX0JVQ0tFVD0ke1MzX1NUT1JBR0VfRklMRVNfQlVDS0VUOi19JwogICAgICAtICdTM19TVE9SQUdFX1NFQVJDSF9CVUNLRVQ9JHtTM19TVE9SQUdFX1NFQVJDSF9CVUNLRVQ6LX0nCiAgICAgIC0gJ1MzX0VORFBPSU5UX1VSTD0ke1MzX0VORFBPSU5UX1VSTDotfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiAnY3VybCAtZiBodHRwOi8vMTI3LjAuMC4xOjMyMTAvdmVyc2lvbicKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHN0YXJ0X3BlcmlvZDogMTBzCiAgZGFzaGJvYXJkOgogICAgaW1hZ2U6ICdnaGNyLmlvL2dldC1jb252ZXgvY29udmV4LWRhc2hib2FyZDphOWE3NjBjYTEwMzk5ZWQ0MmUxYjRiYjg3Yzc4NTM5YTIzNTQ4OGM3JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0RBU0hCT0FSRF82NzkxCiAgICAgIC0gJ05FWFRfUFVCTElDX0RFUExPWU1FTlRfVVJMPSR7U0VSVklDRV9GUUROX0JBQ0tFTkR9JwogICAgZGVwZW5kc19vbjoKICAgICAgYmFja2VuZDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6ICdjdXJsIC1mIGh0dHA6Ly8xMjcuMC4wLjE6Njc5MS8nCiAgICAgIGludGVydmFsOiA1cwogICAgICBzdGFydF9wZXJpb2Q6IDVzCg==", "tags": [ "database", "reactive", @@ -1608,7 +1608,7 @@ "getoutline": { "documentation": "https://docs.getoutline.com/s/hosting/doc/hosting-outline-nipGaCRBDu?utm_source=coolify.io", "slogan": "Your team\u2019s knowledge base", - "compose": "c2VydmljZXM6CiAgb3V0bGluZToKICAgIGltYWdlOiAnZG9ja2VyLmdldG91dGxpbmUuY29tL291dGxpbmV3aWtpL291dGxpbmU6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnc3RvcmFnZS1kYXRhOi92YXIvbGliL291dGxpbmUvZGF0YScKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHJlZGlzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fT1VUTElORV8zMDAwCiAgICAgIC0gTk9ERV9FTlY9cHJvZHVjdGlvbgogICAgICAtICdTRUNSRVRfS0VZPSR7U0VSVklDRV9IRVhfMzJfT1VUTElORX0nCiAgICAgIC0gJ1VUSUxTX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfT1VUTElORX0nCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3JlczovLyR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfToke1NFUlZJQ0VfUEFTU1dPUkRfNjRfUE9TVEdSRVN9QHBvc3RncmVzOjU0MzIvJHtQT1NUR1JFU19EQVRBQkFTRTotb3V0bGluZX0nCiAgICAgIC0gJ1JFRElTX1VSTD1yZWRpczovLzoke1NFUlZJQ0VfUEFTU1dPUkRfNjRfUkVESVN9QHJlZGlzOjYzNzknCiAgICAgIC0gJ1VSTD0ke1NFUlZJQ0VfRlFETl9PVVRMSU5FfScKICAgICAgLSAnUE9SVD0ke09VVExJTkVfUE9SVDotMzAwMH0nCiAgICAgIC0gJ0ZJTEVfU1RPUkFHRT0ke0ZJTEVfU1RPUkFHRTotbG9jYWx9JwogICAgICAtICdGSUxFX1NUT1JBR0VfTE9DQUxfUk9PVF9ESVI9JHtGSUxFX1NUT1JBR0VfTE9DQUxfUk9PVF9ESVI6LS92YXIvbGliL291dGxpbmUvZGF0YX0nCiAgICAgIC0gJ0ZJTEVfU1RPUkFHRV9VUExPQURfTUFYX1NJWkU9JHtGSUxFX1NUT1JBR0VfVVBMT0FEX01BWF9TSVpFOi0yMDAwfScKICAgICAgLSAnRklMRV9TVE9SQUdFX0lNUE9SVF9NQVhfU0laRT0ke0ZJTEVfU1RPUkFHRV9JTVBPUlRfTUFYX1NJWkU6LTEwMH0nCiAgICAgIC0gJ0ZJTEVfU1RPUkFHRV9XT1JLU1BBQ0VfSU1QT1JUX01BWF9TSVpFPSR7RklMRV9TVE9SQUdFX1dPUktTUEFDRV9JTVBPUlRfTUFYX1NJWkV9JwogICAgICAtICdBV1NfQUNDRVNTX0tFWV9JRD0ke0FXU19BQ0NFU1NfS0VZX0lEfScKICAgICAgLSAnQVdTX1NFQ1JFVF9BQ0NFU1NfS0VZPSR7QVdTX1NFQ1JFVF9BQ0NFU1NfS0VZfScKICAgICAgLSAnQVdTX1JFR0lPTj0ke0FXU19SRUdJT059JwogICAgICAtICdBV1NfUzNfQUNDRUxFUkFURV9VUkw9JHtBV1NfUzNfQUNDRUxFUkFURV9VUkx9JwogICAgICAtICdBV1NfUzNfVVBMT0FEX0JVQ0tFVF9VUkw9JHtBV1NfUzNfVVBMT0FEX0JVQ0tFVF9VUkx9JwogICAgICAtICdBV1NfUzNfVVBMT0FEX0JVQ0tFVF9OQU1FPSR7QVdTX1MzX1VQTE9BRF9CVUNLRVRfTkFNRX0nCiAgICAgIC0gJ0FXU19TM19GT1JDRV9QQVRIX1NUWUxFPSR7QVdTX1MzX0ZPUkNFX1BBVEhfU1RZTEU6LXRydWV9JwogICAgICAtICdBV1NfUzNfQUNMPSR7QVdTX1MzX0FDTDotcHJpdmF0ZX0nCiAgICAgIC0gJ1NMQUNLX0NMSUVOVF9JRD0ke1NMQUNLX0NMSUVOVF9JRH0nCiAgICAgIC0gJ1NMQUNLX0NMSUVOVF9TRUNSRVQ9JHtTTEFDS19DTElFTlRfU0VDUkVUfScKICAgICAgLSAnR09PR0xFX0NMSUVOVF9JRD0ke0dPT0dMRV9DTElFTlRfSUR9JwogICAgICAtICdHT09HTEVfQ0xJRU5UX1NFQ1JFVD0ke0dPT0dMRV9DTElFTlRfU0VDUkVUfScKICAgICAgLSAnQVpVUkVfQ0xJRU5UX0lEPSR7QVpVUkVfQ0xJRU5UX0lEfScKICAgICAgLSAnQVpVUkVfQ0xJRU5UX1NFQ1JFVD0ke0FaVVJFX0NMSUVOVF9TRUNSRVR9JwogICAgICAtICdBWlVSRV9SRVNPVVJDRV9BUFBfSUQ9JHtBWlVSRV9SRVNPVVJDRV9BUFBfSUR9JwogICAgICAtICdPSURDX0NMSUVOVF9JRD0ke09JRENfQ0xJRU5UX0lEfScKICAgICAgLSAnT0lEQ19DTElFTlRfU0VDUkVUPSR7T0lEQ19DTElFTlRfU0VDUkVUfScKICAgICAgLSAnT0lEQ19BVVRIX1VSST0ke09JRENfQVVUSF9VUkl9JwogICAgICAtICdPSURDX1RPS0VOX1VSST0ke09JRENfVE9LRU5fVVJJfScKICAgICAgLSAnT0lEQ19VU0VSSU5GT19VUkk9JHtPSURDX1VTRVJJTkZPX1VSSX0nCiAgICAgIC0gJ09JRENfTE9HT1VUX1VSST0ke09JRENfTE9HT1VUX1VSSX0nCiAgICAgIC0gJ09JRENfVVNFUk5BTUVfQ0xBSU09JHtPSURDX1VTRVJOQU1FX0NMQUlNfScKICAgICAgLSAnT0lEQ19ESVNQTEFZX05BTUU9JHtPSURDX0RJU1BMQVlfTkFNRX0nCiAgICAgIC0gJ09JRENfU0NPUEVTPSR7T0lEQ19TQ09QRVN9JwogICAgICAtICdHSVRIVUJfQ0xJRU5UX0lEPSR7R0lUSFVCX0NMSUVOVF9JRH0nCiAgICAgIC0gJ0dJVEhVQl9DTElFTlRfU0VDUkVUPSR7R0lUSFVCX0NMSUVOVF9TRUNSRVR9JwogICAgICAtICdHSVRIVUJfQVBQX05BTUU9JHtHSVRIVUJfQVBQX05BTUV9JwogICAgICAtICdHSVRIVUJfQVBQX0lEPSR7R0lUSFVCX0FQUF9JRH0nCiAgICAgIC0gJ0dJVEhVQl9BUFBfUFJJVkFURV9LRVk9JHtHSVRIVUJfQVBQX1BSSVZBVEVfS0VZfScKICAgICAgLSAnRElTQ09SRF9DTElFTlRfSUQ9JHtESVNDT1JEX0NMSUVOVF9JRH0nCiAgICAgIC0gJ0RJU0NPUkRfQ0xJRU5UX1NFQ1JFVD0ke0RJU0NPUkRfQ0xJRU5UX1NFQ1JFVH0nCiAgICAgIC0gJ0RJU0NPUkRfU0VSVkVSX0lEPSR7RElTQ09SRF9TRVJWRVJfSUR9JwogICAgICAtICdESVNDT1JEX1NFUlZFUl9ST0xFUz0ke0RJU0NPUkRfU0VSVkVSX1JPTEVTfScKICAgICAgLSAnUEdTU0xNT0RFPSR7UEdTU0xNT0RFOi1kaXNhYmxlfScKICAgICAgLSAnRk9SQ0VfSFRUUFM9JHtGT1JDRV9IVFRQUzotdHJ1ZX0nCiAgICAgIC0gJ1NNVFBfSE9TVD0ke1NNVFBfSE9TVH0nCiAgICAgIC0gJ1NNVFBfUE9SVD0ke1NNVFBfUE9SVH0nCiAgICAgIC0gJ1NNVFBfVVNFUk5BTUU9JHtTTVRQX1VTRVJOQU1FfScKICAgICAgLSAnU01UUF9QQVNTV09SRD0ke1NNVFBfUEFTU1dPUkR9JwogICAgICAtICdTTVRQX0ZST01fRU1BSUw9JHtTTVRQX0ZST01fRU1BSUx9JwogICAgICAtICdTTVRQX1JFUExZX0VNQUlMPSR7U01UUF9SRVBMWV9FTUFJTH0nCiAgICAgIC0gJ1NNVFBfVExTX0NJUEhFUlM9JHtTTVRQX1RMU19DSVBIRVJTfScKICAgICAgLSAnU01UUF9TRUNVUkU9JHtTTVRQX1NFQ1VSRX0nCiAgICAgIC0gJ1NNVFBfTkFNRT0ke1NNVFBfTkFNRX0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgZGlzYWJsZTogdHJ1ZQogIHJlZGlzOgogICAgaW1hZ2U6ICdyZWRpczphbHBpbmUnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUkVESVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEXzY0X1JFRElTfScKICAgIGNvbW1hbmQ6CiAgICAgIC0gcmVkaXMtc2VydmVyCiAgICAgIC0gJy0tcmVxdWlyZXBhc3MnCiAgICAgIC0gJyR7U0VSVklDRV9QQVNTV09SRF82NF9SRURJU30nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSAnLWEnCiAgICAgICAgLSAnJHtTRVJWSUNFX1BBU1NXT1JEXzY0X1JFRElTfScKICAgICAgICAtIFBJTkcKICAgICAgaW50ZXJ2YWw6IDEwcwogICAgICB0aW1lb3V0OiAzMHMKICAgICAgcmV0cmllczogMwogIHBvc3RncmVzOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxMi1hbHBpbmUnCiAgICB2b2x1bWVzOgogICAgICAtICdkYXRhYmFzZS1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfScKICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEXzY0X1BPU1RHUkVTfScKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQVRBQkFTRTotb3V0bGluZX0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcGdfaXNyZWFkeQogICAgICAgIC0gJy1VJwogICAgICAgIC0gJyR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfScKICAgICAgICAtICctZCcKICAgICAgICAtICcke1BPU1RHUkVTX0RBVEFCQVNFOi1vdXRsaW5lfScKICAgICAgaW50ZXJ2YWw6IDMwcwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMwo=", + "compose": "c2VydmljZXM6CiAgb3V0bGluZToKICAgIGltYWdlOiAnZG9ja2VyLmdldG91dGxpbmUuY29tL291dGxpbmV3aWtpL291dGxpbmU6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnc3RvcmFnZS1kYXRhOi92YXIvbGliL291dGxpbmUvZGF0YScKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHJlZGlzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fT1VUTElORV8zMDAwCiAgICAgIC0gTk9ERV9FTlY9cHJvZHVjdGlvbgogICAgICAtICdTRUNSRVRfS0VZPSR7U0VSVklDRV9IRVhfNjRfT1VUTElORX0nCiAgICAgIC0gJ1VUSUxTX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfT1VUTElORX0nCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3JlczovLyR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfToke1NFUlZJQ0VfUEFTU1dPUkRfNjRfUE9TVEdSRVN9QHBvc3RncmVzOjU0MzIvJHtQT1NUR1JFU19EQVRBQkFTRTotb3V0bGluZX0nCiAgICAgIC0gJ1JFRElTX1VSTD1yZWRpczovLzoke1NFUlZJQ0VfUEFTU1dPUkRfNjRfUkVESVN9QHJlZGlzOjYzNzknCiAgICAgIC0gJ1VSTD0ke1NFUlZJQ0VfRlFETl9PVVRMSU5FfScKICAgICAgLSAnUE9SVD0ke09VVExJTkVfUE9SVDotMzAwMH0nCiAgICAgIC0gJ0ZJTEVfU1RPUkFHRT0ke0ZJTEVfU1RPUkFHRTotbG9jYWx9JwogICAgICAtICdGSUxFX1NUT1JBR0VfTE9DQUxfUk9PVF9ESVI9JHtGSUxFX1NUT1JBR0VfTE9DQUxfUk9PVF9ESVI6LS92YXIvbGliL291dGxpbmUvZGF0YX0nCiAgICAgIC0gJ0ZJTEVfU1RPUkFHRV9VUExPQURfTUFYX1NJWkU9JHtGSUxFX1NUT1JBR0VfVVBMT0FEX01BWF9TSVpFOi0yMDAwfScKICAgICAgLSAnRklMRV9TVE9SQUdFX0lNUE9SVF9NQVhfU0laRT0ke0ZJTEVfU1RPUkFHRV9JTVBPUlRfTUFYX1NJWkU6LTEwMH0nCiAgICAgIC0gJ0ZJTEVfU1RPUkFHRV9XT1JLU1BBQ0VfSU1QT1JUX01BWF9TSVpFPSR7RklMRV9TVE9SQUdFX1dPUktTUEFDRV9JTVBPUlRfTUFYX1NJWkV9JwogICAgICAtICdBV1NfQUNDRVNTX0tFWV9JRD0ke0FXU19BQ0NFU1NfS0VZX0lEfScKICAgICAgLSAnQVdTX1NFQ1JFVF9BQ0NFU1NfS0VZPSR7QVdTX1NFQ1JFVF9BQ0NFU1NfS0VZfScKICAgICAgLSAnQVdTX1JFR0lPTj0ke0FXU19SRUdJT059JwogICAgICAtICdBV1NfUzNfQUNDRUxFUkFURV9VUkw9JHtBV1NfUzNfQUNDRUxFUkFURV9VUkx9JwogICAgICAtICdBV1NfUzNfVVBMT0FEX0JVQ0tFVF9VUkw9JHtBV1NfUzNfVVBMT0FEX0JVQ0tFVF9VUkx9JwogICAgICAtICdBV1NfUzNfVVBMT0FEX0JVQ0tFVF9OQU1FPSR7QVdTX1MzX1VQTE9BRF9CVUNLRVRfTkFNRX0nCiAgICAgIC0gJ0FXU19TM19GT1JDRV9QQVRIX1NUWUxFPSR7QVdTX1MzX0ZPUkNFX1BBVEhfU1RZTEU6LXRydWV9JwogICAgICAtICdBV1NfUzNfQUNMPSR7QVdTX1MzX0FDTDotcHJpdmF0ZX0nCiAgICAgIC0gJ1NMQUNLX0NMSUVOVF9JRD0ke1NMQUNLX0NMSUVOVF9JRH0nCiAgICAgIC0gJ1NMQUNLX0NMSUVOVF9TRUNSRVQ9JHtTTEFDS19DTElFTlRfU0VDUkVUfScKICAgICAgLSAnR09PR0xFX0NMSUVOVF9JRD0ke0dPT0dMRV9DTElFTlRfSUR9JwogICAgICAtICdHT09HTEVfQ0xJRU5UX1NFQ1JFVD0ke0dPT0dMRV9DTElFTlRfU0VDUkVUfScKICAgICAgLSAnQVpVUkVfQ0xJRU5UX0lEPSR7QVpVUkVfQ0xJRU5UX0lEfScKICAgICAgLSAnQVpVUkVfQ0xJRU5UX1NFQ1JFVD0ke0FaVVJFX0NMSUVOVF9TRUNSRVR9JwogICAgICAtICdBWlVSRV9SRVNPVVJDRV9BUFBfSUQ9JHtBWlVSRV9SRVNPVVJDRV9BUFBfSUR9JwogICAgICAtICdPSURDX0NMSUVOVF9JRD0ke09JRENfQ0xJRU5UX0lEfScKICAgICAgLSAnT0lEQ19DTElFTlRfU0VDUkVUPSR7T0lEQ19DTElFTlRfU0VDUkVUfScKICAgICAgLSAnT0lEQ19BVVRIX1VSST0ke09JRENfQVVUSF9VUkl9JwogICAgICAtICdPSURDX1RPS0VOX1VSST0ke09JRENfVE9LRU5fVVJJfScKICAgICAgLSAnT0lEQ19VU0VSSU5GT19VUkk9JHtPSURDX1VTRVJJTkZPX1VSSX0nCiAgICAgIC0gJ09JRENfTE9HT1VUX1VSST0ke09JRENfTE9HT1VUX1VSSX0nCiAgICAgIC0gJ09JRENfVVNFUk5BTUVfQ0xBSU09JHtPSURDX1VTRVJOQU1FX0NMQUlNfScKICAgICAgLSAnT0lEQ19ESVNQTEFZX05BTUU9JHtPSURDX0RJU1BMQVlfTkFNRX0nCiAgICAgIC0gJ09JRENfU0NPUEVTPSR7T0lEQ19TQ09QRVN9JwogICAgICAtICdHSVRIVUJfQ0xJRU5UX0lEPSR7R0lUSFVCX0NMSUVOVF9JRH0nCiAgICAgIC0gJ0dJVEhVQl9DTElFTlRfU0VDUkVUPSR7R0lUSFVCX0NMSUVOVF9TRUNSRVR9JwogICAgICAtICdHSVRIVUJfQVBQX05BTUU9JHtHSVRIVUJfQVBQX05BTUV9JwogICAgICAtICdHSVRIVUJfQVBQX0lEPSR7R0lUSFVCX0FQUF9JRH0nCiAgICAgIC0gJ0dJVEhVQl9BUFBfUFJJVkFURV9LRVk9JHtHSVRIVUJfQVBQX1BSSVZBVEVfS0VZfScKICAgICAgLSAnRElTQ09SRF9DTElFTlRfSUQ9JHtESVNDT1JEX0NMSUVOVF9JRH0nCiAgICAgIC0gJ0RJU0NPUkRfQ0xJRU5UX1NFQ1JFVD0ke0RJU0NPUkRfQ0xJRU5UX1NFQ1JFVH0nCiAgICAgIC0gJ0RJU0NPUkRfU0VSVkVSX0lEPSR7RElTQ09SRF9TRVJWRVJfSUR9JwogICAgICAtICdESVNDT1JEX1NFUlZFUl9ST0xFUz0ke0RJU0NPUkRfU0VSVkVSX1JPTEVTfScKICAgICAgLSAnUEdTU0xNT0RFPSR7UEdTU0xNT0RFOi1kaXNhYmxlfScKICAgICAgLSAnRk9SQ0VfSFRUUFM9JHtGT1JDRV9IVFRQUzotdHJ1ZX0nCiAgICAgIC0gJ1NNVFBfSE9TVD0ke1NNVFBfSE9TVH0nCiAgICAgIC0gJ1NNVFBfUE9SVD0ke1NNVFBfUE9SVH0nCiAgICAgIC0gJ1NNVFBfVVNFUk5BTUU9JHtTTVRQX1VTRVJOQU1FfScKICAgICAgLSAnU01UUF9QQVNTV09SRD0ke1NNVFBfUEFTU1dPUkR9JwogICAgICAtICdTTVRQX0ZST01fRU1BSUw9JHtTTVRQX0ZST01fRU1BSUx9JwogICAgICAtICdTTVRQX1JFUExZX0VNQUlMPSR7U01UUF9SRVBMWV9FTUFJTH0nCiAgICAgIC0gJ1NNVFBfVExTX0NJUEhFUlM9JHtTTVRQX1RMU19DSVBIRVJTfScKICAgICAgLSAnU01UUF9TRUNVUkU9JHtTTVRQX1NFQ1VSRX0nCiAgICAgIC0gJ1NNVFBfTkFNRT0ke1NNVFBfTkFNRX0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgZGlzYWJsZTogdHJ1ZQogIHJlZGlzOgogICAgaW1hZ2U6ICdyZWRpczphbHBpbmUnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUkVESVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEXzY0X1JFRElTfScKICAgIGNvbW1hbmQ6CiAgICAgIC0gcmVkaXMtc2VydmVyCiAgICAgIC0gJy0tcmVxdWlyZXBhc3MnCiAgICAgIC0gJyR7U0VSVklDRV9QQVNTV09SRF82NF9SRURJU30nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSAnLWEnCiAgICAgICAgLSAnJHtTRVJWSUNFX1BBU1NXT1JEXzY0X1JFRElTfScKICAgICAgICAtIFBJTkcKICAgICAgaW50ZXJ2YWw6IDEwcwogICAgICB0aW1lb3V0OiAzMHMKICAgICAgcmV0cmllczogMwogIHBvc3RncmVzOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxMi1hbHBpbmUnCiAgICB2b2x1bWVzOgogICAgICAtICdkYXRhYmFzZS1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfScKICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEXzY0X1BPU1RHUkVTfScKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQVRBQkFTRTotb3V0bGluZX0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcGdfaXNyZWFkeQogICAgICAgIC0gJy1VJwogICAgICAgIC0gJyR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfScKICAgICAgICAtICctZCcKICAgICAgICAtICcke1BPU1RHUkVTX0RBVEFCQVNFOi1vdXRsaW5lfScKICAgICAgaW50ZXJ2YWw6IDMwcwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMwo=", "tags": [ "knowledge base", "documentation" @@ -2000,7 +2000,7 @@ "homarr": { "documentation": "https://homarr.dev?utm_source=coolify.io", "slogan": "Homarr is a self-hosted homepage for your services.", - "compose": "c2VydmljZXM6CiAgaG9tYXJyOgogICAgaW1hZ2U6ICdnaGNyLmlvL2hvbWFyci1sYWJzL2hvbWFycjp2MS40MC4wJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0hPTUFSUl83NTc1CiAgICAgIC0gU0VSVklDRV9IRVhfMzJfSE9NQVJSCiAgICAgIC0gJ1NFQ1JFVF9FTkNSWVBUSU9OX0tFWT0ke1NFUlZJQ0VfSEVYXzMyX0hPTUFSUn0nCiAgICB2b2x1bWVzOgogICAgICAtICcvdmFyL3J1bi9kb2NrZXIuc29jazovdmFyL3J1bi9kb2NrZXIuc29jaycKICAgICAgLSAnLi9ob21hcnIvYXBwZGF0YTovYXBwZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSB3Z2V0CiAgICAgICAgLSAnLXEnCiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo3NTc1JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==", + "compose": "c2VydmljZXM6CiAgaG9tYXJyOgogICAgaW1hZ2U6ICdnaGNyLmlvL2hvbWFyci1sYWJzL2hvbWFycjp2MS40MC4wJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0hPTUFSUl83NTc1CiAgICAgIC0gJ1NFQ1JFVF9FTkNSWVBUSU9OX0tFWT0ke1NFUlZJQ0VfSEVYXzY0X0hPTUFSUn0nCiAgICB2b2x1bWVzOgogICAgICAtICcvdmFyL3J1bi9kb2NrZXIuc29jazovdmFyL3J1bi9kb2NrZXIuc29jaycKICAgICAgLSAnLi9ob21hcnIvYXBwZGF0YTovYXBwZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSB3Z2V0CiAgICAgICAgLSAnLXEnCiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo3NTc1JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==", "tags": [ "homarr", "self-hosted", @@ -3408,7 +3408,7 @@ "open-archiver": { "documentation": "https://docs.openarchiver.com/?utm_source=coolify.io", "slogan": "A self-hosted, open-source email archiving solution with full-text search capability.", - "compose": "c2VydmljZXM6CiAgb3Blbi1hcmNoaXZlcjoKICAgIGltYWdlOiAnbG9naWNsYWJzaHEvb3Blbi1hcmNoaXZlcjpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fT1BFTkFSQ0hJVkVSXzMwMDAKICAgICAgLSAnRU5DUllQVElPTl9LRVk9JHtTRVJWSUNFX0hFWF8zMl9FTkNSWVBUSU9OS0VZfScKICAgICAgLSAnU1RPUkFHRV9FTkNSWVBUSU9OX0tFWT0ke1NFUlZJQ0VfSEVYXzMyX1NUT1JBR0VFTkNSWVBUSU9OS0VZfScKICAgICAgLSAnUE9SVF9CQUNLRU5EPSR7UE9SVF9CQUNLRU5EOi00MDAwfScKICAgICAgLSAnUE9SVF9GUk9OVEVORD0ke1BPUlRfRlJPTlRFTkQ6LTMwMDB9JwogICAgICAtICdOT0RFX0VOVj0ke05PREVfRU5WOi1wcm9kdWN0aW9ufScKICAgICAgLSAnU1lOQ19GUkVRVUVOQ1k9JHtTWU5DX0ZSRVFVRU5DWTotKiAqICogKiAqfScKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotb3Blbl9hcmNoaXZlfScKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3Jlc3FsOi8vJHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9OiR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU31AcG9zdGdyZXM6NTQzMi8ke1BPU1RHUkVTX0RCOi1vcGVuLWFyY2hpdmVyLWRifScKICAgICAgLSAnTUVJTElfTUFTVEVSX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfTUVJTElTRUFSQ0h9JwogICAgICAtICdNRUlMSV9IT1NUPWh0dHA6Ly9tZWlsaXNlYXJjaDo3NzAwJwogICAgICAtIFJFRElTX0hPU1Q9dmFsa2V5CiAgICAgIC0gUkVESVNfUE9SVD02Mzc5CiAgICAgIC0gJ1JFRElTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9WQUxLRVl9JwogICAgICAtIFJFRElTX1RMU19FTkFCTEVEPWZhbHNlCiAgICAgIC0gJ1NUT1JBR0VfVFlQRT0ke1NUT1JBR0VfVFlQRTotbG9jYWx9JwogICAgICAtICdTVE9SQUdFX0xPQ0FMX1JPT1RfUEFUSD0ke1NUT1JBR0VfTE9DQUxfUk9PVF9QQVRIOi0vdmFyL2RhdGEvb3Blbi1hcmNoaXZlcn0nCiAgICAgIC0gJ0JPRFlfU0laRV9MSU1JVD0ke0JPRFlfU0laRV9MSU1JVDotMTAwTX0nCiAgICAgIC0gJ1NUT1JBR0VfUzNfRU5EUE9JTlQ9JHtTVE9SQUdFX1MzX0VORFBPSU5UfScKICAgICAgLSAnU1RPUkFHRV9TM19CVUNLRVQ9JHtTVE9SQUdFX1MzX0JVQ0tFVH0nCiAgICAgIC0gJ1NUT1JBR0VfUzNfQUNDRVNTX0tFWV9JRD0ke1NUT1JBR0VfUzNfQUNDRVNTX0tFWV9JRH0nCiAgICAgIC0gJ1NUT1JBR0VfUzNfU0VDUkVUX0FDQ0VTU19LRVk9JHtTVE9SQUdFX1MzX1NFQ1JFVF9BQ0NFU1NfS0VZfScKICAgICAgLSAnU1RPUkFHRV9TM19SRUdJT049JHtTVE9SQUdFX1MzX1JFR0lPTn0nCiAgICAgIC0gJ1NUT1JBR0VfUzNfRk9SQ0VfUEFUSF9TVFlMRT0ke1NUT1JBR0VfUzNfRk9SQ0VfUEFUSF9TVFlMRTotZmFsc2V9JwogICAgICAtICdKV1RfU0VDUkVUPSR7U0VSVklDRV9CQVNFNjRfMTI4X0pXVH0nCiAgICAgIC0gJ0pXVF9FWFBJUkVTX0lOPSR7SldUX0VYUElSRVNfSU46LTdkfScKICAgICAgLSAnUkFURV9MSU1JVF9XSU5ET1dfTVM9JHtSQVRFX0xJTUlUX1dJTkRPV19NUzotNjAwMDB9JwogICAgICAtICdSQVRFX0xJTUlUX01BWF9SRVFVRVNUUz0ke1JBVEVfTElNSVRfTUFYX1JFUVVFU1RTOi0xMDB9JwogICAgdm9sdW1lczoKICAgICAgLSAnYXJjaGl2ZXItZGF0YTovdmFyL2RhdGEvb3Blbi1hcmNoaXZlcicKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHZhbGtleToKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICBtZWlsaXNlYXJjaDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogIHBvc3RncmVzOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNy1hbHBpbmUnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotb3Blbi1hcmNoaXZlci1kYn0nCiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogICAgICAtIExDX0FMTD1DCiAgICB2b2x1bWVzOgogICAgICAtICdwb3N0Z3Jlcy1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiAxMHMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgdmFsa2V5OgogICAgaW1hZ2U6ICd2YWxrZXkvdmFsa2V5OjgtYWxwaW5lJwogICAgY29tbWFuZDogJ3ZhbGtleS1zZXJ2ZXIgLS1yZXF1aXJlcGFzcyAke1NFUlZJQ0VfUEFTU1dPUkRfVkFMS0VZfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3ZhbGtleS1kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHJlZGlzLWNsaQogICAgICAgIC0gcGluZwogICAgICBpbnRlcnZhbDogMTBzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDMKICBtZWlsaXNlYXJjaDoKICAgIGltYWdlOiAnZ2V0bWVpbGkvbWVpbGlzZWFyY2g6djEuMTUnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTUVJTElfTUFTVEVSX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfTUVJTElTRUFSQ0h9JwogICAgdm9sdW1lczoKICAgICAgLSAnbWVpbGlzZWFyY2gtZGF0YTovbWVpbGlfZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo3NzAwL2hlYWx0aCcKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=", + "compose": "c2VydmljZXM6CiAgb3Blbi1hcmNoaXZlcjoKICAgIGltYWdlOiAnbG9naWNsYWJzaHEvb3Blbi1hcmNoaXZlcjpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fT1BFTkFSQ0hJVkVSXzMwMDAKICAgICAgLSAnRU5DUllQVElPTl9LRVk9JHtTRVJWSUNFX0hFWF82NF9FTkNSWVBUSU9OS0VZfScKICAgICAgLSAnU1RPUkFHRV9FTkNSWVBUSU9OX0tFWT0ke1NFUlZJQ0VfSEVYXzY0X1NUT1JBR0VFTkNSWVBUSU9OS0VZfScKICAgICAgLSAnUE9SVF9CQUNLRU5EPSR7UE9SVF9CQUNLRU5EOi00MDAwfScKICAgICAgLSAnUE9SVF9GUk9OVEVORD0ke1BPUlRfRlJPTlRFTkQ6LTMwMDB9JwogICAgICAtICdOT0RFX0VOVj0ke05PREVfRU5WOi1wcm9kdWN0aW9ufScKICAgICAgLSAnU1lOQ19GUkVRVUVOQ1k9JHtTWU5DX0ZSRVFVRU5DWTotKiAqICogKiAqfScKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotb3Blbl9hcmNoaXZlfScKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3Jlc3FsOi8vJHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9OiR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU31AcG9zdGdyZXM6NTQzMi8ke1BPU1RHUkVTX0RCOi1vcGVuLWFyY2hpdmVyLWRifScKICAgICAgLSAnTUVJTElfTUFTVEVSX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfTUVJTElTRUFSQ0h9JwogICAgICAtICdNRUlMSV9IT1NUPWh0dHA6Ly9tZWlsaXNlYXJjaDo3NzAwJwogICAgICAtIFJFRElTX0hPU1Q9dmFsa2V5CiAgICAgIC0gUkVESVNfUE9SVD02Mzc5CiAgICAgIC0gJ1JFRElTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9WQUxLRVl9JwogICAgICAtIFJFRElTX1RMU19FTkFCTEVEPWZhbHNlCiAgICAgIC0gJ1NUT1JBR0VfVFlQRT0ke1NUT1JBR0VfVFlQRTotbG9jYWx9JwogICAgICAtICdTVE9SQUdFX0xPQ0FMX1JPT1RfUEFUSD0ke1NUT1JBR0VfTE9DQUxfUk9PVF9QQVRIOi0vdmFyL2RhdGEvb3Blbi1hcmNoaXZlcn0nCiAgICAgIC0gJ0JPRFlfU0laRV9MSU1JVD0ke0JPRFlfU0laRV9MSU1JVDotMTAwTX0nCiAgICAgIC0gJ1NUT1JBR0VfUzNfRU5EUE9JTlQ9JHtTVE9SQUdFX1MzX0VORFBPSU5UfScKICAgICAgLSAnU1RPUkFHRV9TM19CVUNLRVQ9JHtTVE9SQUdFX1MzX0JVQ0tFVH0nCiAgICAgIC0gJ1NUT1JBR0VfUzNfQUNDRVNTX0tFWV9JRD0ke1NUT1JBR0VfUzNfQUNDRVNTX0tFWV9JRH0nCiAgICAgIC0gJ1NUT1JBR0VfUzNfU0VDUkVUX0FDQ0VTU19LRVk9JHtTVE9SQUdFX1MzX1NFQ1JFVF9BQ0NFU1NfS0VZfScKICAgICAgLSAnU1RPUkFHRV9TM19SRUdJT049JHtTVE9SQUdFX1MzX1JFR0lPTn0nCiAgICAgIC0gJ1NUT1JBR0VfUzNfRk9SQ0VfUEFUSF9TVFlMRT0ke1NUT1JBR0VfUzNfRk9SQ0VfUEFUSF9TVFlMRTotZmFsc2V9JwogICAgICAtICdKV1RfU0VDUkVUPSR7U0VSVklDRV9CQVNFNjRfMTI4X0pXVH0nCiAgICAgIC0gJ0pXVF9FWFBJUkVTX0lOPSR7SldUX0VYUElSRVNfSU46LTdkfScKICAgICAgLSAnUkFURV9MSU1JVF9XSU5ET1dfTVM9JHtSQVRFX0xJTUlUX1dJTkRPV19NUzotNjAwMDB9JwogICAgICAtICdSQVRFX0xJTUlUX01BWF9SRVFVRVNUUz0ke1JBVEVfTElNSVRfTUFYX1JFUVVFU1RTOi0xMDB9JwogICAgdm9sdW1lczoKICAgICAgLSAnYXJjaGl2ZXItZGF0YTovdmFyL2RhdGEvb3Blbi1hcmNoaXZlcicKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHZhbGtleToKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICBtZWlsaXNlYXJjaDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogIHBvc3RncmVzOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNy1hbHBpbmUnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotb3Blbi1hcmNoaXZlci1kYn0nCiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogICAgICAtIExDX0FMTD1DCiAgICB2b2x1bWVzOgogICAgICAtICdwb3N0Z3Jlcy1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiAxMHMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgdmFsa2V5OgogICAgaW1hZ2U6ICd2YWxrZXkvdmFsa2V5OjgtYWxwaW5lJwogICAgY29tbWFuZDogJ3ZhbGtleS1zZXJ2ZXIgLS1yZXF1aXJlcGFzcyAke1NFUlZJQ0VfUEFTU1dPUkRfVkFMS0VZfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3ZhbGtleS1kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHJlZGlzLWNsaQogICAgICAgIC0gcGluZwogICAgICBpbnRlcnZhbDogMTBzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDMKICBtZWlsaXNlYXJjaDoKICAgIGltYWdlOiAnZ2V0bWVpbGkvbWVpbGlzZWFyY2g6djEuMTUnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTUVJTElfTUFTVEVSX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfTUVJTElTRUFSQ0h9JwogICAgdm9sdW1lczoKICAgICAgLSAnbWVpbGlzZWFyY2gtZGF0YTovbWVpbGlfZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo3NzAwL2hlYWx0aCcKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=", "tags": [ "email archiving", "email", diff --git a/tests/Feature/Security/ApiSensitiveFieldsTest.php b/tests/Feature/Security/ApiSensitiveFieldsTest.php index 2275f7cc2..591f227cb 100644 --- a/tests/Feature/Security/ApiSensitiveFieldsTest.php +++ b/tests/Feature/Security/ApiSensitiveFieldsTest.php @@ -196,4 +196,14 @@ function makeTeamUser(): array expect($body)->toContain('"internal_db_url":'); expect($body)->toContain('"sentinel_token":'); }); + + test('project database list can eager load nested destination server settings', function () { + $databases = $this->project->databases(['destination.server.settings']); + $database = $databases->firstWhere('id', $this->database->id); + + expect($database)->not->toBeNull() + ->and($database->relationLoaded('destination'))->toBeTrue() + ->and($database->destination->relationLoaded('server'))->toBeTrue() + ->and($database->destination->server->relationLoaded('settings'))->toBeTrue(); + }); }); diff --git a/tests/Unit/Models/SensitiveFieldsHiddenTest.php b/tests/Unit/Models/SensitiveFieldsHiddenTest.php index 92e73c907..f95539d8a 100644 --- a/tests/Unit/Models/SensitiveFieldsHiddenTest.php +++ b/tests/Unit/Models/SensitiveFieldsHiddenTest.php @@ -48,7 +48,9 @@ 'manual_webhook_secret_gitea', 'dockerfile', 'docker_compose', + 'docker_compose_pr', 'docker_compose_raw', + 'docker_compose_pr_raw', 'custom_labels', ); }); From 12aba41d9a539cc7052ae69c57e6e63979fbedd6 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 11 May 2026 11:53:22 +0200 Subject: [PATCH 04/12] fix(api): gate service server secrets by sensitive scope Only eager load nested server settings for service responses when the API token has read:sensitive, preventing read-only tokens from exposing sentinel fields while preserving sensitive access. --- .../Controllers/Api/ServicesController.php | 18 ++++- .../Security/ApiSensitiveFieldsTest.php | 75 +++++++++++++++++++ 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/Api/ServicesController.php b/app/Http/Controllers/Api/ServicesController.php index c3ced0e31..773fadd78 100644 --- a/app/Http/Controllers/Api/ServicesController.php +++ b/app/Http/Controllers/Api/ServicesController.php @@ -14,6 +14,7 @@ use App\Models\Server; use App\Models\Service; use App\Support\ValidationPatterns; +use Illuminate\Database\Eloquent\Model; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Collection; @@ -50,9 +51,9 @@ private function removeSensitiveData($service) * Handles both single models and Eloquent Collections (the listing endpoint * passes a Collection of Services per project to removeSensitiveData()). */ - private function exposeNestedServerSecrets($model): void + private function exposeNestedServerSecrets(Model|Collection $model): void { - if ($model instanceof Collection || $model instanceof \Illuminate\Database\Eloquent\Collection) { + if ($model instanceof Collection) { foreach ($model as $item) { $this->exposeNestedServerSecrets($item); } @@ -215,8 +216,12 @@ public function services(Request $request) } $projects = Project::where('team_id', $teamId)->get(); $services = collect(); + $serviceRelations = $request->attributes->get('can_read_sensitive', false) === true + ? ['destination.server.settings'] + : []; + foreach ($projects as $project) { - $services->push($project->services()->get()); + $services->push($project->services()->with($serviceRelations)->get()); } foreach ($services as $service) { $service = $this->removeSensitiveData($service); @@ -771,7 +776,12 @@ public function service_by_uuid(Request $request) $this->authorize('view', $service); - $service = $service->load(['applications', 'databases']); + $serviceRelations = ['applications', 'databases']; + if ($request->attributes->get('can_read_sensitive', false) === true) { + $serviceRelations[] = 'destination.server.settings'; + } + + $service = $service->load($serviceRelations); return response()->json($this->removeSensitiveData($service)); } diff --git a/tests/Feature/Security/ApiSensitiveFieldsTest.php b/tests/Feature/Security/ApiSensitiveFieldsTest.php index 591f227cb..0036b8546 100644 --- a/tests/Feature/Security/ApiSensitiveFieldsTest.php +++ b/tests/Feature/Security/ApiSensitiveFieldsTest.php @@ -5,6 +5,7 @@ use App\Models\InstanceSettings; use App\Models\Project; use App\Models\Server; +use App\Models\Service; use App\Models\StandalonePostgresql; use App\Models\Team; use App\Models\User; @@ -207,3 +208,77 @@ function makeTeamUser(): array ->and($database->destination->server->relationLoaded('settings'))->toBeTrue(); }); }); + +describe('GET /api/v1/services sensitive field gating', function () { + beforeEach(function () { + $this->project = Project::factory()->create(['team_id' => $this->team->id]); + $this->environment = Environment::factory()->create(['project_id' => $this->project->id]); + + $destination = $this->server->standaloneDockers()->firstOrFail(); + $this->service = Service::factory()->create([ + 'server_id' => $this->server->id, + 'destination_id' => $destination->id, + 'destination_type' => $destination->getMorphClass(), + 'environment_id' => $this->environment->id, + ]); + }); + + test('read token does not leak service or nested server sensitive fields', function () { + $token = makeApiToken($this->user, $this->team, ['read']); + + $response = $this->withHeaders([ + 'Authorization' => 'Bearer '.$token, + ])->getJson('/api/v1/services'); + + $response->assertStatus(200); + + $body = $response->getContent(); + expect($body)->not->toContain('"docker_compose_raw":') + ->and($body)->not->toContain('"sentinel_token":') + ->and($body)->not->toContain('"sentinel_custom_url":'); + }); + + test('read sensitive token sees service and nested server sensitive fields', function () { + $token = makeApiToken($this->user, $this->team, ['read', 'read:sensitive']); + + $response = $this->withHeaders([ + 'Authorization' => 'Bearer '.$token, + ])->getJson('/api/v1/services'); + + $response->assertStatus(200); + + $body = $response->getContent(); + expect($body)->toContain('"docker_compose_raw":') + ->and($body)->toContain('"sentinel_token":') + ->and($body)->toContain('"sentinel_custom_url":'); + }); + + test('read sensitive service list eager loads nested server settings once', function () { + $secondServer = Server::factory()->create(['team_id' => $this->team->id]); + $secondDestination = $secondServer->standaloneDockers()->firstOrFail(); + + Service::factory()->create([ + 'server_id' => $secondServer->id, + 'destination_id' => $secondDestination->id, + 'destination_type' => $secondDestination->getMorphClass(), + 'environment_id' => $this->environment->id, + ]); + + $token = makeApiToken($this->user, $this->team, ['read', 'read:sensitive']); + $serverSettingsQueries = collect(); + + DB::listen(function ($query) use ($serverSettingsQueries) { + if (str_contains($query->sql, 'from "server_settings"')) { + $serverSettingsQueries->push($query->sql); + } + }); + + $response = $this->withHeaders([ + 'Authorization' => 'Bearer '.$token, + ])->getJson('/api/v1/services'); + + $response->assertStatus(200); + + expect($serverSettingsQueries->contains(fn (string $sql) => str_contains($sql, '"server_settings"."server_id" in')))->toBeTrue(); + }); +}); From f40bb80f9decadce4c33ccf869bc5314e362359e Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 11 May 2026 20:52:29 +0200 Subject: [PATCH 05/12] style(api): tighten nested server secret visibility checks --- app/Http/Controllers/Api/DatabasesController.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/Api/DatabasesController.php b/app/Http/Controllers/Api/DatabasesController.php index 839e52cb3..0d22afbec 100644 --- a/app/Http/Controllers/Api/DatabasesController.php +++ b/app/Http/Controllers/Api/DatabasesController.php @@ -62,17 +62,18 @@ private function removeSensitiveData($database) */ private function exposeNestedServerSecrets(Model $model): void { - $server = $model->destination?->server ?? null; - if (! $server) { + $server = $model->destination?->server; + if ($server === null) { return; } + $server->makeVisible([ 'logdrain_axiom_api_key', 'logdrain_newrelic_license_key', ]); - $settings = $server->settings ?? null; - if ($settings) { - $settings->makeVisible([ + + if ($server->settings !== null) { + $server->settings->makeVisible([ 'sentinel_token', 'sentinel_custom_url', 'logdrain_newrelic_license_key', From 81a3bb0f0769e5a765e77649d002ea6acf9a667f Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 11 May 2026 21:18:07 +0200 Subject: [PATCH 06/12] fix(api): hide sensitive fields by default Add model-level hidden fields for secrets, tokens, keys, notification credentials, deployment logs, and environment values. Allow explicit read:sensitive API access to reveal gated private keys and deployment logs, and cover the behavior with feature and unit tests. --- app/Http/Controllers/Api/DeployController.php | 7 + .../Controllers/Api/SecurityController.php | 4 + app/Models/ApplicationDeploymentQueue.php | 4 + app/Models/CloudInitScript.php | 4 + app/Models/CloudProviderToken.php | 4 + app/Models/DiscordNotificationSettings.php | 4 + app/Models/EmailNotificationSettings.php | 10 ++ app/Models/InstanceSettings.php | 11 ++ app/Models/OauthSetting.php | 4 + app/Models/PrivateKey.php | 4 + app/Models/PushoverNotificationSettings.php | 5 + app/Models/S3Storage.php | 5 + app/Models/SharedEnvironmentVariable.php | 4 + app/Models/SlackNotificationSettings.php | 4 + app/Models/SslCertificate.php | 4 + app/Models/TelegramNotificationSettings.php | 22 ++- app/Models/WebhookNotificationSettings.php | 4 + .../components/notification/navbar.blade.php | 2 +- .../Security/ApiSensitiveFieldsTest.php | 95 ++++++++++++ .../Unit/Models/SensitiveFieldsHiddenTest.php | 136 ++++++++++++++++++ 20 files changed, 335 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Api/DeployController.php b/app/Http/Controllers/Api/DeployController.php index c93731d68..7a049ae2b 100644 --- a/app/Http/Controllers/Api/DeployController.php +++ b/app/Http/Controllers/Api/DeployController.php @@ -25,6 +25,10 @@ private function removeSensitiveData($deployment) $deployment->makeHidden([ 'logs', ]); + } else { + $deployment->makeVisible([ + 'logs', + ]); } return serializeApiResponse($deployment); @@ -699,6 +703,9 @@ public function get_application_deployments(Request $request) $this->authorize('view', $application); $deployments = $application->deployments($skip, $take); + if ($request->attributes->get('can_read_sensitive', false) === true) { + $deployments['deployments']->each->makeVisible(['logs']); + } return response()->json($deployments); } diff --git a/app/Http/Controllers/Api/SecurityController.php b/app/Http/Controllers/Api/SecurityController.php index e59c40866..759af5134 100644 --- a/app/Http/Controllers/Api/SecurityController.php +++ b/app/Http/Controllers/Api/SecurityController.php @@ -16,6 +16,10 @@ private function removeSensitiveData($team) $team->makeHidden([ 'private_key', ]); + } else { + $team->makeVisible([ + 'private_key', + ]); } return serializeApiResponse($team); diff --git a/app/Models/ApplicationDeploymentQueue.php b/app/Models/ApplicationDeploymentQueue.php index 67f28523c..f4c334bf3 100644 --- a/app/Models/ApplicationDeploymentQueue.php +++ b/app/Models/ApplicationDeploymentQueue.php @@ -68,6 +68,10 @@ class ApplicationDeploymentQueue extends Model 'finished_at', ]; + protected $hidden = [ + 'logs', + ]; + protected $casts = [ 'pull_request_id' => 'integer', 'finished_at' => 'datetime', diff --git a/app/Models/CloudInitScript.php b/app/Models/CloudInitScript.php index 2c78cc582..9d2676aca 100644 --- a/app/Models/CloudInitScript.php +++ b/app/Models/CloudInitScript.php @@ -12,6 +12,10 @@ class CloudInitScript extends Model 'script', ]; + protected $hidden = [ + 'script', + ]; + protected function casts(): array { return [ diff --git a/app/Models/CloudProviderToken.php b/app/Models/CloudProviderToken.php index 026d11fba..02c662d9d 100644 --- a/app/Models/CloudProviderToken.php +++ b/app/Models/CloudProviderToken.php @@ -11,6 +11,10 @@ class CloudProviderToken extends BaseModel 'name', ]; + protected $hidden = [ + 'token', + ]; + protected $casts = [ 'token' => 'encrypted', ]; diff --git a/app/Models/DiscordNotificationSettings.php b/app/Models/DiscordNotificationSettings.php index e86598126..135c921f6 100644 --- a/app/Models/DiscordNotificationSettings.php +++ b/app/Models/DiscordNotificationSettings.php @@ -34,6 +34,10 @@ class DiscordNotificationSettings extends Model 'discord_ping_enabled', ]; + protected $hidden = [ + 'discord_webhook_url', + ]; + protected $casts = [ 'discord_enabled' => 'boolean', 'discord_webhook_url' => 'encrypted', diff --git a/app/Models/EmailNotificationSettings.php b/app/Models/EmailNotificationSettings.php index 1277e45d9..7368bafbf 100644 --- a/app/Models/EmailNotificationSettings.php +++ b/app/Models/EmailNotificationSettings.php @@ -43,6 +43,16 @@ class EmailNotificationSettings extends Model 'traefik_outdated_email_notifications', ]; + protected $hidden = [ + 'smtp_from_address', + 'smtp_from_name', + 'smtp_recipients', + 'smtp_host', + 'smtp_username', + 'smtp_password', + 'resend_api_key', + ]; + protected $casts = [ 'smtp_enabled' => 'boolean', 'smtp_from_address' => 'encrypted', diff --git a/app/Models/InstanceSettings.php b/app/Models/InstanceSettings.php index d5c3bfa28..d63611fac 100644 --- a/app/Models/InstanceSettings.php +++ b/app/Models/InstanceSettings.php @@ -48,6 +48,17 @@ class InstanceSettings extends Model 'is_mcp_server_enabled', ]; + protected $hidden = [ + 'smtp_from_address', + 'smtp_from_name', + 'smtp_recipients', + 'smtp_host', + 'smtp_username', + 'smtp_password', + 'resend_api_key', + 'sentinel_token', + ]; + protected $casts = [ 'smtp_enabled' => 'boolean', 'smtp_from_address' => 'encrypted', diff --git a/app/Models/OauthSetting.php b/app/Models/OauthSetting.php index 08e08d85b..e7999134a 100644 --- a/app/Models/OauthSetting.php +++ b/app/Models/OauthSetting.php @@ -13,6 +13,10 @@ class OauthSetting extends Model protected $fillable = ['provider', 'client_id', 'client_secret', 'redirect_uri', 'tenant', 'base_url', 'enabled']; + protected $hidden = [ + 'client_secret', + ]; + protected function clientSecret(): Attribute { return Attribute::make( diff --git a/app/Models/PrivateKey.php b/app/Models/PrivateKey.php index 1521678f3..ee330aa19 100644 --- a/app/Models/PrivateKey.php +++ b/app/Models/PrivateKey.php @@ -41,6 +41,10 @@ class PrivateKey extends BaseModel 'fingerprint', ]; + protected $hidden = [ + 'private_key', + ]; + protected $casts = [ 'private_key' => 'encrypted', ]; diff --git a/app/Models/PushoverNotificationSettings.php b/app/Models/PushoverNotificationSettings.php index 5ad617ad6..dd0d81cc0 100644 --- a/app/Models/PushoverNotificationSettings.php +++ b/app/Models/PushoverNotificationSettings.php @@ -34,6 +34,11 @@ class PushoverNotificationSettings extends Model 'traefik_outdated_pushover_notifications', ]; + protected $hidden = [ + 'pushover_user_key', + 'pushover_api_token', + ]; + protected $casts = [ 'pushover_enabled' => 'boolean', 'pushover_user_key' => 'encrypted', diff --git a/app/Models/S3Storage.php b/app/Models/S3Storage.php index 3f6ee51cc..dbc895825 100644 --- a/app/Models/S3Storage.php +++ b/app/Models/S3Storage.php @@ -26,6 +26,11 @@ class S3Storage extends BaseModel 'unusable_email_sent', ]; + protected $hidden = [ + 'key', + 'secret', + ]; + protected $casts = [ 'is_usable' => 'boolean', 'key' => 'encrypted', diff --git a/app/Models/SharedEnvironmentVariable.php b/app/Models/SharedEnvironmentVariable.php index eadc33ec2..8bb241240 100644 --- a/app/Models/SharedEnvironmentVariable.php +++ b/app/Models/SharedEnvironmentVariable.php @@ -30,6 +30,10 @@ class SharedEnvironmentVariable extends Model 'version', ]; + protected $hidden = [ + 'value', + ]; + protected $casts = [ 'key' => 'string', 'value' => 'encrypted', diff --git a/app/Models/SlackNotificationSettings.php b/app/Models/SlackNotificationSettings.php index d4f125fb5..62603685e 100644 --- a/app/Models/SlackNotificationSettings.php +++ b/app/Models/SlackNotificationSettings.php @@ -33,6 +33,10 @@ class SlackNotificationSettings extends Model 'traefik_outdated_slack_notifications', ]; + protected $hidden = [ + 'slack_webhook_url', + ]; + protected $casts = [ 'slack_enabled' => 'boolean', 'slack_webhook_url' => 'encrypted', diff --git a/app/Models/SslCertificate.php b/app/Models/SslCertificate.php index eb2175d44..2311cea72 100644 --- a/app/Models/SslCertificate.php +++ b/app/Models/SslCertificate.php @@ -20,6 +20,10 @@ class SslCertificate extends Model 'is_ca_certificate', ]; + protected $hidden = [ + 'ssl_private_key', + ]; + protected $casts = [ 'ssl_certificate' => 'encrypted', 'ssl_private_key' => 'encrypted', diff --git a/app/Models/TelegramNotificationSettings.php b/app/Models/TelegramNotificationSettings.php index 4930f45d4..8c644f9bc 100644 --- a/app/Models/TelegramNotificationSettings.php +++ b/app/Models/TelegramNotificationSettings.php @@ -49,6 +49,25 @@ class TelegramNotificationSettings extends Model 'telegram_notifications_traefik_outdated_thread_id', ]; + protected $hidden = [ + 'telegram_token', + 'telegram_chat_id', + 'telegram_notifications_deployment_success_thread_id', + 'telegram_notifications_deployment_failure_thread_id', + 'telegram_notifications_status_change_thread_id', + 'telegram_notifications_backup_success_thread_id', + 'telegram_notifications_backup_failure_thread_id', + 'telegram_notifications_scheduled_task_success_thread_id', + 'telegram_notifications_scheduled_task_failure_thread_id', + 'telegram_notifications_docker_cleanup_success_thread_id', + 'telegram_notifications_docker_cleanup_failure_thread_id', + 'telegram_notifications_server_disk_usage_thread_id', + 'telegram_notifications_server_reachable_thread_id', + 'telegram_notifications_server_unreachable_thread_id', + 'telegram_notifications_server_patch_thread_id', + 'telegram_notifications_traefik_outdated_thread_id', + ]; + protected $casts = [ 'telegram_enabled' => 'boolean', 'telegram_token' => 'encrypted', @@ -75,7 +94,8 @@ class TelegramNotificationSettings extends Model 'telegram_notifications_backup_failure_thread_id' => 'encrypted', 'telegram_notifications_scheduled_task_success_thread_id' => 'encrypted', 'telegram_notifications_scheduled_task_failure_thread_id' => 'encrypted', - 'telegram_notifications_docker_cleanup_thread_id' => 'encrypted', + 'telegram_notifications_docker_cleanup_success_thread_id' => 'encrypted', + 'telegram_notifications_docker_cleanup_failure_thread_id' => 'encrypted', 'telegram_notifications_server_disk_usage_thread_id' => 'encrypted', 'telegram_notifications_server_reachable_thread_id' => 'encrypted', 'telegram_notifications_server_unreachable_thread_id' => 'encrypted', diff --git a/app/Models/WebhookNotificationSettings.php b/app/Models/WebhookNotificationSettings.php index 731006181..c6a81b50a 100644 --- a/app/Models/WebhookNotificationSettings.php +++ b/app/Models/WebhookNotificationSettings.php @@ -33,6 +33,10 @@ class WebhookNotificationSettings extends Model 'traefik_outdated_webhook_notifications', ]; + protected $hidden = [ + 'webhook_url', + ]; + protected function casts(): array { return [ diff --git a/resources/views/components/notification/navbar.blade.php b/resources/views/components/notification/navbar.blade.php index 0ee3b8ee4..256c4d528 100644 --- a/resources/views/components/notification/navbar.blade.php +++ b/resources/views/components/notification/navbar.blade.php @@ -2,7 +2,7 @@