diff --git a/apps/federation/appinfo/info.xml b/apps/federation/appinfo/info.xml
index 2c9bce9612e8a..f497f44f43a53 100644
--- a/apps/federation/appinfo/info.xml
+++ b/apps/federation/appinfo/info.xml
@@ -30,6 +30,7 @@
+ OCA\Federation\Command\ManageTrustedServers
OCA\Federation\Command\SyncFederationAddressBooks
OCA\Federation\Command\SyncFederationCalendars
diff --git a/apps/federation/lib/Command/ManageTrustedServers.php b/apps/federation/lib/Command/ManageTrustedServers.php
new file mode 100644
index 0000000000000..adc1760f87780
--- /dev/null
+++ b/apps/federation/lib/Command/ManageTrustedServers.php
@@ -0,0 +1,161 @@
+ 'ok',
+ TrustedServers::STATUS_PENDING => 'pending',
+ TrustedServers::STATUS_FAILURE => 'failure',
+ TrustedServers::STATUS_ACCESS_REVOKED => 'access revoked',
+ ];
+
+ public function __construct(
+ private TrustedServers $trustedServersManager,
+ ) {
+ parent::__construct();
+ }
+
+ #[\Override]
+ protected function configure() {
+ $this
+ ->setName('federation:trusted-servers')
+ ->setDescription('Manage trusted servers')
+ ->addArgument(
+ 'servers',
+ InputArgument::IS_ARRAY,
+ 'space-separated list of server URLs, used with --add or --remove'
+ )
+ ->addOption('add', null, InputOption::VALUE_NONE, 'add the given servers to the trusted servers list')
+ ->addOption('remove', null, InputOption::VALUE_NONE, 'remove the given servers from the trusted servers list')
+ ->addOption('list', null, InputOption::VALUE_NONE, 'list all trusted servers');
+ }
+
+ #[\Override]
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ $add = (bool)$input->getOption('add');
+ $remove = (bool)$input->getOption('remove');
+ $list = (bool)$input->getOption('list');
+
+ if (($add ? 1 : 0) + ($remove ? 1 : 0) + ($list ? 1 : 0) !== 1) {
+ $output->writeln('Please specify exactly one of --add, --remove or --list.');
+ return self::INVALID;
+ }
+
+ if ($list) {
+ return $this->listServers($output);
+ }
+
+ $servers = $input->getArgument('servers');
+ if (empty($servers)) {
+ $output->writeln('You must provide at least one server URL.');
+ return self::INVALID;
+ }
+
+ return $add
+ ? $this->addServers($servers, $output)
+ : $this->removeServers($servers, $output);
+ }
+
+ private function listServers(OutputInterface $output): int {
+ $servers = $this->trustedServersManager->getServers();
+
+ if (empty($servers)) {
+ $output->writeln('No trusted servers configured.');
+ return self::SUCCESS;
+ }
+
+ $table = new Table($output);
+ $table->setHeaders(['id', 'url', 'status']);
+ foreach ($servers as $server) {
+ $table->addRow([
+ $server['id'],
+ $server['url'],
+ self::STATUS_LABELS[$server['status']] ?? (string)$server['status'],
+ ]);
+ }
+ $table->render();
+
+ return self::SUCCESS;
+ }
+
+ private function addServers(array $servers, OutputInterface $output): int {
+ $output->writeln('Adding trusted servers:');
+ $hasFailure = false;
+ foreach ($servers as $server) {
+ if ($this->trustedServersManager->isTrustedServer($server)) {
+ $output->writeln(" - $server already trusted, skipping");
+ continue;
+ }
+
+ try {
+ $this->trustedServersManager->addServer($server);
+ $output->writeln(" - $server added");
+ } catch (\Exception $e) {
+ $output->writeln(" - $server failed: " . $e->getMessage());
+ $hasFailure = true;
+ }
+ }
+
+ return $hasFailure ? self::FAILURE : self::SUCCESS;
+ }
+
+ private function removeServers(array $servers, OutputInterface $output): int {
+ $output->writeln('Removing trusted servers:');
+ $hasFailure = false;
+ foreach ($servers as $server) {
+ $id = $this->findServerId($server);
+ if ($id === null) {
+ $output->writeln(" - $server not found, skipping");
+ continue;
+ }
+
+ try {
+ $this->trustedServersManager->removeServer($id);
+ $output->writeln(" - $server removed");
+ } catch (\Exception $e) {
+ $output->writeln(" - $server failed: " . $e->getMessage());
+ $hasFailure = true;
+ }
+ }
+
+ return $hasFailure ? self::FAILURE : self::SUCCESS;
+ }
+
+ /**
+ * Trusted servers are stored with a normalized (https:// prefixed,
+ * trailing-slash trimmed) URL, so removal has to look up the id by
+ * matching that same normalization rather than the raw user input.
+ */
+ private function findServerId(string $url): ?int {
+ $normalized = rtrim(
+ (str_starts_with($url, 'http://') || str_starts_with($url, 'https://'))
+ ? $url
+ : 'https://' . $url,
+ '/'
+ );
+
+ foreach ($this->trustedServersManager->getServers() ?? [] as $server) {
+ if (rtrim($server['url'], '/') === $normalized) {
+ return $server['id'];
+ }
+ }
+
+ return null;
+ }
+}