diff --git a/web/lib/potato_mesh/application/federation.rb b/web/lib/potato_mesh/application/federation.rb index a1d8e58..f256ca8 100644 --- a/web/lib/potato_mesh/application/federation.rb +++ b/web/lib/potato_mesh/application/federation.rb @@ -250,6 +250,9 @@ module PotatoMesh end def start_federation_announcer! + # Federation broadcasts must not execute when federation support is disabled. + return nil unless federation_enabled? + existing = settings.federation_thread return existing if existing&.alive? @@ -277,6 +280,9 @@ module PotatoMesh # # @return [Thread, nil] the thread handling the initial announcement. def start_initial_federation_announcement! + # Skip the initial broadcast entirely when federation is disabled. + return nil unless federation_enabled? + existing = settings.respond_to?(:initial_federation_thread) ? settings.initial_federation_thread : nil return existing if existing&.alive? diff --git a/web/lib/potato_mesh/application/routes/api.rb b/web/lib/potato_mesh/application/routes/api.rb index e59f5f8..a79f183 100644 --- a/web/lib/potato_mesh/application/routes/api.rb +++ b/web/lib/potato_mesh/application/routes/api.rb @@ -125,6 +125,9 @@ module PotatoMesh end app.get "/api/instances" do + # Prevent the federation catalog from being exposed when federation is disabled. + halt 404 unless federation_enabled? + content_type :json ensure_self_instance_record! payload = load_instances_for_api diff --git a/web/spec/app_spec.rb b/web/spec/app_spec.rb index 73d0e12..d88afd1 100644 --- a/web/spec/app_spec.rb +++ b/web/spec/app_spec.rb @@ -280,6 +280,40 @@ RSpec.describe "Potato Mesh Sinatra app" do expect(result).to be(dummy_thread) expect(app.settings.federation_thread).to be(dummy_thread) end + + context "when federation is disabled" do + around do |example| + original = ENV["FEDERATION"] + begin + ENV["FEDERATION"] = "0" + example.run + ensure + if original.nil? + ENV.delete("FEDERATION") + else + ENV["FEDERATION"] = original + end + end + end + + it "does not start the initial announcement thread" do + expect(Thread).not_to receive(:new) + + result = app.start_initial_federation_announcement! + + expect(result).to be_nil + expect(app.settings.respond_to?(:initial_federation_thread) ? app.settings.initial_federation_thread : nil).to be_nil + end + + it "does not start the recurring announcer thread" do + expect(Thread).not_to receive(:new) + + result = app.start_federation_announcer! + + expect(result).to be_nil + expect(app.settings.federation_thread).to be_nil + end + end end before do @@ -2023,6 +2057,28 @@ RSpec.describe "Potato Mesh Sinatra app" do expect(domains).to eq(["duplicate.example"]) end end + + context "when federation is disabled" do + around do |example| + original = ENV["FEDERATION"] + begin + ENV["FEDERATION"] = "0" + example.run + ensure + if original.nil? + ENV.delete("FEDERATION") + else + ENV["FEDERATION"] = original + end + end + end + + it "returns 404" do + get "/api/instances" + + expect(last_response.status).to eq(404) + end + end end describe "POST /api/nodes" do