BareGit

Add config option for sock file permission

Author: MetroWind <chris.corsair@gmail.com>
Date: Fri Sep 11 15:03:41 2026 -0700
Commit: 1e7eb55f6845d8b98d1aa14aeb072e97f250e9df

Changes

diff --git a/src/config.cpp b/src/config.cpp
index c8ed81c..9142eee 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -28,6 +28,7 @@ const std::set<std::string> CONFIG_KEYS = {
     "email",
     "listen_address",
     "listen_port",
+    "listen_socket_permission",
     "maximum_accumulated_pulls",
     "static_root",
     "thumbnail_long_side",
@@ -237,6 +238,24 @@ mw::E<Config> loadConfig(const std::filesystem::path& config_path)
     base_url->path(base_path.c_str());
 
     std::optional<mw::HTTPServer::ListenAddress> listen_address;
+    std::optional<unsigned int> listen_socket_permission;
+    if(table.contains("listen_socket_permission"))
+    {
+        auto permission = table["listen_socket_permission"].value<
+            std::int64_t>();
+        if(!permission)
+        {
+            return std::unexpected(mw::runtimeError(
+                "Configuration key 'listen_socket_permission' must be an "
+                "integer"));
+        }
+        if(*permission < 0 || *permission > 0777)
+        {
+            return std::unexpected(mw::runtimeError(
+                "listen_socket_permission must be from 0 through 0o777"));
+        }
+        listen_socket_permission = static_cast<unsigned int>(*permission);
+    }
     if(listen_address_text->starts_with("unix:"))
     {
         const std::string socket_path = listen_address_text->substr(5);
@@ -245,7 +264,9 @@ mw::E<Config> loadConfig(const std::filesystem::path& config_path)
             return std::unexpected(mw::runtimeError(
                 "A Unix listen address requires a socket path"));
         }
-        listen_address.emplace(mw::SocketFileInfo(socket_path));
+        mw::SocketFileInfo socket(socket_path);
+        socket.permission = listen_socket_permission;
+        listen_address.emplace(std::move(socket));
     }
     else
     {
diff --git a/tests/config_test.cpp b/tests/config_test.cpp
index 2f90762..d68bfe9 100644
--- a/tests/config_test.cpp
+++ b/tests/config_test.cpp
@@ -111,9 +111,44 @@ TEST(ConfigTest, LoadsUnixSocketConfiguration)
     ASSERT_TRUE(config) << config.error().msg();
     ASSERT_TRUE(std::holds_alternative<mw::SocketFileInfo>(
         config->listen_address));
-    EXPECT_EQ(
-        std::get<mw::SocketFileInfo>(config->listen_address).filename,
-        "/tmp/card-collection.sock");
+    const mw::SocketFileInfo& socket =
+        std::get<mw::SocketFileInfo>(config->listen_address);
+    EXPECT_EQ(socket.filename, "/tmp/card-collection.sock");
+    EXPECT_FALSE(socket.permission.has_value());
+}
+
+/// Verify Unix socket permissions are passed to the HTTP server.
+TEST(ConfigTest, LoadsUnixSocketPermission)
+{
+    TemporaryConfigRoot temporary;
+    const std::filesystem::path config_path = temporary.path() / "app.toml";
+    writeConfig(
+        config_path,
+        "listen_address = \"unix:/tmp/card-collection.sock\"\n"
+        "listen_socket_permission = 0o660\n");
+
+    auto config = loadConfig(config_path);
+
+    ASSERT_TRUE(config) << config.error().msg();
+    ASSERT_TRUE(std::holds_alternative<mw::SocketFileInfo>(
+        config->listen_address));
+    const mw::SocketFileInfo& socket =
+        std::get<mw::SocketFileInfo>(config->listen_address);
+    ASSERT_TRUE(socket.permission.has_value());
+    EXPECT_EQ(*socket.permission, 0660);
+}
+
+/// Reject socket permissions outside the regular permission-bit range.
+TEST(ConfigTest, RejectsInvalidUnixSocketPermission)
+{
+    TemporaryConfigRoot temporary;
+    const std::filesystem::path config_path = temporary.path() / "app.toml";
+    writeConfig(
+        config_path,
+        "listen_address = \"unix:/tmp/card-collection.sock\"\n"
+        "listen_socket_permission = 0o1000\n");
+
+    EXPECT_FALSE(loadConfig(config_path));
 }
 
 /// Verify unknown configuration keys are rejected.