#include "glove/container/digest.hpp" #include "glove/container/receipt_producer.hpp" #include "glove/control/guest_channel.hpp" #include "glove/control/observation_intent_unix_server.hpp" #include "glove/control/session_registry.hpp" #include "glove/supervisor/library_bundle.hpp" #include "glove/supervisor/path_alias.hpp" #include "glove/supervisor/session_plan.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace wire_test { struct enqueue_success { std::uint8_t schema_version = 0; std::string status; std::uint64_t sequence = 0; std::string intent_digest; }; struct enqueue_error { std::uint8_t schema_version = 0; std::string code; }; } // namespace wire_test namespace { #define REQUIRE(cond) \ do { \ if (!(cond)) { \ std::fprintf(stderr, "REQUIRE failed: %s @ %s:%d\n", #cond, __FILE__, __LINE__); \ return 1; \ } \ } while (0) constexpr std::string_view audit_key = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"; constexpr std::string_view controller_digest = "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"; constexpr std::string_view channel_token = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; constexpr std::string_view library_bundle = R"({"schema_version":1,"source_library_ref":"bafy-test","source_manifest_digest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","entries":[]})"; auto write_owner_only(const std::filesystem::path& path, std::string_view value) -> bool { std::ofstream output{path, std::ios::binary | std::ios::trunc}; output << value; output.flush(); return output.good() && ::chmod(path.c_str(), 0600) == 0; } class temporary_directory { public: temporary_directory() { std::string pattern = "/tmp/glove-observation-server-test-XXXXXX"; if (char* created = ::mkdtemp(pattern.data()); created != nullptr) { root_ = created; } } temporary_directory(const temporary_directory&) = delete; auto operator=(const temporary_directory&) -> temporary_directory& = delete; temporary_directory(temporary_directory&& other) noexcept : root_{std::move(other.root_)} {} auto operator=(temporary_directory&& other) noexcept -> temporary_directory& { if (this != &other) { std::error_code error; std::filesystem::remove_all(root_, error); root_ = std::move(other.root_); } return *this; } ~temporary_directory() { std::error_code error; std::filesystem::remove_all(root_, error); } [[nodiscard]] auto root() const -> const std::filesystem::path& { return root_; } private: std::filesystem::path root_; }; class unique_fd { public: explicit unique_fd(int value = -1) noexcept : value_{value} {} unique_fd(const unique_fd&) = delete; auto operator=(const unique_fd&) -> unique_fd& = delete; unique_fd(unique_fd&& other) noexcept : value_{std::exchange(other.value_, -1)} {} auto operator=(unique_fd&&) -> unique_fd& = delete; ~unique_fd() { if (value_ >= 0) { ::close(value_); } } [[nodiscard]] auto get() const noexcept -> int { return value_; } private: int value_; }; auto library_bundle_digest() -> std::string { const auto* bytes = reinterpret_cast(library_bundle.data()); return glove::container::sha256_hex(std::span{bytes, library_bundle.size()}).value_or(""); } auto launch_template() -> glove::supervisor::runtime_launch_template { return { .runtime_discovery = {}, .executable_path = "/usr/bin/true", .executable_search_paths = {}, .arguments = {"--version"}, .environment = {"PATH=/usr/bin:/bin", "TERM=xterm-256color"}, .read_only_paths = {}, }; } auto runtime_digest() -> std::string { return glove::supervisor::runtime_launch_template_digest(launch_template()).value_or(""); } auto valid_plan_at(std::uint64_t now_ms) -> std::string { return R"({"schema_version":1,"runtime_id":"codex","runtime_template_id":"codex-safe","adapter_command_digest":")" + runtime_digest() + R"(","sandbox_backend":"linux_production","egress_policy_id":"no-network","tool_policy_id":"sage-readonly","path_grants":[{"alias":"workspace","access":"ephemeral_write","materialization":"copy","max_bytes":1048576,"ttl_secs":60,"cleanup_policy":"remove"}],"library_projections":[{"projection_id":"sage-core","content_digest":")" + library_bundle_digest() + R"(","destination_alias":"libraries"}],"secret_handles":["codex-token"],"limits":{"cpu_time_ms":1000,"memory_bytes":67108864,"pids":16,"wall_time_ms":2000,"disk_bytes":2097152,"terminal_output_bytes":1048576},"policy_revision":7,"expires_at_ms":)" + std::to_string(now_ms + 120'000) + "}"; } auto sage_guest_plan_at(std::uint64_t now_ms) -> std::string { auto plan = valid_plan_at(now_ms); for (const auto& [from, to] : { std::pair{ std::string_view{"\"runtime_id\":\"codex\""}, std::string_view{"\"runtime_id\":\"sage-guest\""} }, std::pair{ std::string_view{"\"runtime_template_id\":\"codex-safe\""}, std::string_view{"\"runtime_template_id\":\"sage-guest-safe\""} }, std::pair{ std::string_view{"\"secret_handles\":[\"codex-token\"]"}, std::string_view{"\"secret_handles\":[]"} }, }) { const auto offset = plan.find(from); if (offset != std::string::npos) { plan.replace(offset, from.size(), to); } } return plan; } // Registration example (test fixture): the host owns payload semantics. auto test_channel_host() -> std::shared_ptr { auto host = std::make_shared(); (void)host->register_channel({ .channel_id = "test-observation", .schema_id = "sage.glove-observation.v1", .body_validator = [](const glove::control::glove_observation_body&) { return true; }, .bounds = { .max_items = 4'096, .max_body_bytes = 8'192, .max_ttl_ms = 600'000, .max_skew_ms = 30'000, }, }); return host; } auto validator_for(const std::filesystem::path& source) -> glove::supervisor::result { using namespace glove::supervisor; auto paths = path_alias_registry::build({ path_alias_policy{ .alias = "workspace", .host_path = std::filesystem::canonical(source).string(), .target_path = "/workspace", .max_ttl_secs = 120, .access = { path_access_policy{ .access = path_access::ephemeral_write, .materialization = path_materialization::copy, .create_policy = path_create_policy::empty_directory, .cleanup_policy = path_cleanup_policy::remove, .max_bytes = 2'097'152, }, }, }, }); if (!paths) { return std::unexpected(paths.error()); } return session_plan_validator::build( session_plan_policy{ .revision = 7, .max_plan_ttl_ms = 120'000, .runtime_templates = { runtime_template_policy{ .runtime_template_id = "codex-safe", .runtime_id = "codex", .adapter_command_digest = runtime_digest(), .backend = sandbox_backend::linux_production, .allowed_path_aliases = {"workspace"}, .allowed_projection_destinations = {"libraries"}, .launch = launch_template(), .adoption = std::nullopt, }, runtime_template_policy{ .runtime_template_id = "sage-guest-safe", .runtime_id = "sage-guest", .adapter_command_digest = runtime_digest(), .backend = sandbox_backend::linux_production, .allowed_path_aliases = {"workspace"}, .allowed_projection_destinations = {"libraries"}, .launch = launch_template(), .adoption = std::nullopt, }, }, .library_projection_destinations = { library_projection_destination_policy{ .alias = "libraries", .target_path = "/opt/sage/library-bundles", }, }, .resource_profiles = { resource_limits{ .cpu_time_ms = 1'000, .memory_bytes = 67'108'864, .pids = 16, .wall_time_ms = 2'000, .disk_bytes = 2'097'152, .terminal_output_bytes = 1'048'576, }, }, .egress_policy_ids = {"no-network"}, .tool_policy_ids = {"sage-readonly"}, .secret_handles = {"codex-token"}, .egress_policies = {}, .secret_mounts = {}, }, std::move(*paths) ); } auto cgroup_identity(std::uint32_t pid) -> glove::control::linux_cgroup_recovery_identity { return { .schema_version = 1, .device = 42, .inode = 20'000U + pid, }; } auto filesystem_identity() -> glove::control::linux_filesystem_recovery_identity { return { .schema_version = 1, .disk_limit_bytes = 2'097'152, .partitions = {{.alias = "workspace", .quota_bytes = 1'048'576}}, }; } auto process_identity(std::uint32_t pid) -> glove::control::linux_process_identity { return { .schema_version = 1, .pid = pid, .boot_id = "12345678-1234-1234-1234-123456789abc", .start_time_ticks = 10'000U + pid, .cgroup_device = 42, .cgroup_inode = 20'000U + pid, .cgroup_path_digest = std::string(64, 'd'), }; } struct running_session_fixture { temporary_directory temp; std::shared_ptr registry; std::shared_ptr producer; std::string session_id; std::string profile_digest; std::string projection_digest; std::uint64_t start_ms = 0; running_session_fixture(const running_session_fixture&) = delete; auto operator=(const running_session_fixture&) -> running_session_fixture& = delete; running_session_fixture(running_session_fixture&&) = default; auto operator=(running_session_fixture&&) -> running_session_fixture& = default; running_session_fixture() = default; }; auto open_running_session(std::uint64_t start_ms) -> std::optional { running_session_fixture fixture; fixture.start_ms = start_ms; if (fixture.temp.root().empty()) { return std::nullopt; } const auto source = fixture.temp.root() / "source"; if (!std::filesystem::create_directory(source)) { return std::nullopt; } auto validator = validator_for(source); if (!validator) { return std::nullopt; } auto shared_validator = std::make_shared(std::move(*validator)); const auto bundle_root = fixture.temp.root() / "library-bundles"; if (!std::filesystem::create_directory(bundle_root)) { return std::nullopt; } if (::chmod(bundle_root.c_str(), 0700) != 0) { return std::nullopt; } const auto bundle_path = bundle_root / (library_bundle_digest() + ".json"); { std::ofstream output{bundle_path, std::ios::binary}; output.write(library_bundle.data(), static_cast(library_bundle.size())); } if (::chmod(bundle_path.c_str(), 0600) != 0) { return std::nullopt; } auto opened_bundle_store = glove::supervisor::library_bundle_store::open(bundle_root); if (!opened_bundle_store) { return std::nullopt; } auto shared_bundle_store = std::make_shared( std::move(*opened_bundle_store) ); const auto audit_key_path = fixture.temp.root() / "receipt.key"; { std::ofstream output{audit_key_path, std::ios::binary | std::ios::trunc}; output << audit_key << '\n'; } if (::chmod(audit_key_path.c_str(), 0600) != 0) { return std::nullopt; } auto producer = glove::container::receipt_audit_producer::initialize({ .key_path = audit_key_path, .journal_path = fixture.temp.root() / "receipts.journal", }); if (!producer) { return std::nullopt; } if (!(*producer)->acknowledge_bootstrap((*producer)->anchor()).has_value()) { return std::nullopt; } fixture.producer = *producer; auto registry = glove::control::session_registry::open_or_create( fixture.temp.root() / "sessions.journal", shared_validator, shared_bundle_store, glove::control::default_session_registry_bytes, test_channel_host() ); if (!registry) { return std::nullopt; } fixture.registry = std::move(*registry); fixture.session_id = "observation-session"; fixture.profile_digest = std::string(64, 'a'); fixture.projection_digest = std::string(64, 'c'); auto created = fixture.registry->create( fixture.session_id, controller_digest, sage_guest_plan_at(start_ms), "create-observation-session", start_ms ); if (!created) { return std::nullopt; } const glove::control::session_start_authorization authorization{ .schema_version = 1, .authorization_id = "approval-observation-session", .session_id = fixture.session_id, .controller_plan_digest = std::string{controller_digest}, .plan_content_digest = created->plan_content_digest, .approved_at_ms = start_ms + 1U, .expires_at_ms = start_ms + 60'000U, }; auto reserved = fixture.registry->reserve_start( authorization, "reserve-observation-session", start_ms + 2U ); if (!reserved) { return std::nullopt; } const glove::control::session_execution_binding binding{ .schema_version = 1, .session_id = fixture.session_id, .controller_plan_digest = std::string{controller_digest}, .plan_content_digest = created->plan_content_digest, .authorization_id = authorization.authorization_id, .profile_digest = fixture.profile_digest, .cgroup_identity = cgroup_identity(7001), .filesystem_identity = filesystem_identity(), }; auto reservation = fixture.producer->reserve_terminal( binding.session_id, binding.controller_plan_digest, binding.profile_digest ); if (!reservation) { return std::nullopt; } auto starting = fixture.registry->mark_starting( binding, *reservation, "starting-observation-session", start_ms + 3U ); if (!starting) { return std::nullopt; } const glove::control::session_running_commitment running{ .schema_version = 1, .session_id = fixture.session_id, .controller_plan_digest = binding.controller_plan_digest, .plan_content_digest = binding.plan_content_digest, .authorization_id = binding.authorization_id, .profile_digest = fixture.profile_digest, .process_identity = process_identity(7001), .filesystem_identity = binding.filesystem_identity, }; if (!fixture.registry ->mark_running(running, *reservation, "running-observation-session", start_ms + 4U) .has_value()) { return std::nullopt; } return std::optional{std::move(fixture)}; } auto make_enqueue_request( std::string_view token, std::string_view intent_id = "intent-1", std::string_view observation = "guest-capability-inventory", std::string_view value_digest = "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", std::uint64_t item_count = 4, std::optional extra_field = std::nullopt ) -> std::string { std::string request = std::string{"{\"schema_version\":1,\"channel_token\":\""} + std::string{token} + "\",\"body\":{\"schema\":\"sage.glove-observation.v1\",\"intent_id\":\"" + std::string{intent_id} + "\",\"observation\":\"" + std::string{observation} + "\",\"value_digest\":\"" + std::string{value_digest} + "\",\"item_count\":" + std::to_string(item_count) + "}"; if (extra_field) { request += ",\"" + std::string{*extra_field} + "\":1"; } request += "}"; return request; } auto write_exact(int descriptor, const void* input, std::size_t size) -> bool { const auto* bytes = static_cast(input); std::size_t written = 0; while (written < size) { const auto result = ::write(descriptor, bytes + written, size - written); if (result < 0 && errno == EINTR) { continue; } if (result <= 0) { return false; } written += static_cast(result); } return true; } auto read_exact(int descriptor, void* output, std::size_t size) -> bool { auto* bytes = static_cast(output); std::size_t consumed = 0; while (consumed < size) { const auto result = ::read(descriptor, bytes + consumed, size - consumed); if (result < 0 && errno == EINTR) { continue; } if (result <= 0) { return false; } consumed += static_cast(result); } return true; } auto connect_to(const std::filesystem::path& socket_path) -> unique_fd { #if defined(__linux__) unique_fd descriptor{::socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)}; #else unique_fd descriptor{::socket(AF_UNIX, SOCK_STREAM, 0)}; if (descriptor.get() >= 0) { (void)::fcntl(descriptor.get(), F_SETFD, FD_CLOEXEC); } #endif if (descriptor.get() < 0) { return unique_fd{}; } #if defined(SO_NOSIGPIPE) const int enabled = 1; if (::setsockopt(descriptor.get(), SOL_SOCKET, SO_NOSIGPIPE, &enabled, sizeof(enabled)) != 0) { return unique_fd{}; } #endif ::sockaddr_un address{}; address.sun_family = AF_UNIX; const auto value = socket_path.string(); if (value.size() >= sizeof(address.sun_path)) { return unique_fd{}; } std::memcpy(address.sun_path, value.c_str(), value.size() + 1U); if (::connect( descriptor.get(), reinterpret_cast(&address), sizeof(address) ) != 0) { return unique_fd{}; } return descriptor; } auto transact(const std::filesystem::path& socket_path, std::string_view frame) -> std::optional { auto descriptor = connect_to(socket_path); if (descriptor.get() < 0) { return std::nullopt; } const auto size = htonl(static_cast(frame.size())); if (!write_exact(descriptor.get(), &size, sizeof(size))) { return std::nullopt; } // An oversized-frame rejection may arrive before the peer drains the // caller's body. Preserve the response even when that final write closes. static_cast(write_exact(descriptor.get(), frame.data(), frame.size())); std::uint32_t response_size = 0; if (!read_exact(descriptor.get(), &response_size, sizeof(response_size))) { return std::nullopt; } const auto decoded_size = ntohl(response_size); if (decoded_size == 0 || decoded_size > glove::control::max_observation_frame_bytes) { return std::nullopt; } std::string response(decoded_size, '\0'); if (!read_exact(descriptor.get(), response.data(), response.size())) { return std::nullopt; } return response; } auto decode_success(std::string_view frame) -> std::optional { wire_test::enqueue_success value{}; if (glz::read(value, frame)) { return std::nullopt; } return value; } auto decode_error(std::string_view frame) -> std::optional { wire_test::enqueue_error value{}; if (glz::read(value, frame)) { return std::nullopt; } return value; } auto response_is_redacted(std::string_view frame) -> bool { return frame.find(channel_token) == std::string_view::npos && frame.find("\"channel_token\"") == std::string_view::npos && frame.find("observation-session") == std::string_view::npos && frame.find("secret_handles") == std::string_view::npos && frame.find("path_grants") == std::string_view::npos && frame.find("runtime_template_id") == std::string_view::npos && frame.find("intent-source") == std::string_view::npos; } auto server_config_for( const running_session_fixture& fixture, const std::filesystem::path& socket_path, std::uint64_t session_expires_at_ms, std::uint64_t io_timeout_ms = 100 ) -> glove::control::observation_intent_unix_server_config { return { .socket_path = socket_path, .sessions = fixture.registry.get(), .session_id = fixture.session_id, .runtime_id = "sage-guest", .controller_plan_digest = std::string{controller_digest}, .profile_digest = fixture.profile_digest, .projection_digest = fixture.projection_digest, .policy_revision = 7, .service_channel_id = "observation-session-observation-v1", .channel_generation = 1, .session_expires_at_ms = session_expires_at_ms, .channel_token = std::string{channel_token}, .io_timeout_ms = io_timeout_ms, .expected_peer_uid = ::geteuid(), }; } auto serve_in_background( glove::control::observation_intent_unix_server& server, std::size_t count, std::optional& error_out ) -> std::thread { return std::thread{[&server, count, &error_out] { for (std::size_t index = 0; index < count; ++index) { auto served = server.serve_one_for(5'000); if (!served) { std::fprintf(stderr, "observation server failed: %s\n", served.error().c_str()); error_out = served.error(); return; } if (!*served) { error_out = "accept deadline elapsed"; return; } } }}; } auto run() -> int { // F5: transient accept-boundary errors are retryable; fatal config and // descriptor-state errors are not. REQUIRE(glove::control::observation_transient_accept_error(EINTR)); REQUIRE(glove::control::observation_transient_accept_error(EAGAIN)); REQUIRE(glove::control::observation_transient_accept_error(ECONNABORTED)); REQUIRE(glove::control::observation_transient_accept_error(EMFILE)); REQUIRE(glove::control::observation_transient_accept_error(ENFILE)); REQUIRE(glove::control::observation_transient_accept_error(ENOBUFS)); REQUIRE(glove::control::observation_transient_accept_error(ENOMEM)); REQUIRE(!glove::control::observation_transient_accept_error(EBADF)); REQUIRE(!glove::control::observation_transient_accept_error(EINVAL)); REQUIRE(!glove::control::observation_transient_accept_error(EACCES)); const auto now_ms = []() -> std::uint64_t { using namespace std::chrono; const auto ms = duration_cast(system_clock::now().time_since_epoch()); return ms.count() < 0 ? 0U : static_cast(ms.count()); }(); const std::uint64_t start_ms = now_ms > 10'000U ? now_ms - 10'000U : 1U; auto fixture = open_running_session(start_ms); REQUIRE(fixture.has_value()); const auto socket_path = fixture->temp.root() / "observation.sock"; auto config = server_config_for(*fixture, socket_path, start_ms + 120'000U); auto server = glove::control::observation_intent_unix_server::create(config); REQUIRE(server.has_value()); REQUIRE((*server)->socket_path() == socket_path); struct stat socket_status{}; REQUIRE(::lstat(socket_path.c_str(), &socket_status) == 0); REQUIRE(S_ISSOCK(socket_status.st_mode)); REQUIRE(socket_status.st_uid == ::geteuid()); REQUIRE((socket_status.st_mode & 0777U) == 0600U); std::optional serve_error; auto server_thread = serve_in_background(**server, 1, serve_error); const auto request = make_enqueue_request(channel_token); auto response = transact(socket_path, request); server_thread.join(); REQUIRE(!serve_error.has_value()); REQUIRE(response.has_value()); REQUIRE(response_is_redacted(*response)); auto success = decode_success(*response); REQUIRE(success.has_value()); REQUIRE(success->schema_version == 1); REQUIRE(success->status == "queued"); REQUIRE(success->sequence != 0); REQUIRE(success->intent_digest.size() == 64U); const auto count_after_enqueue = fixture->registry->record_count(); serve_error.reset(); server_thread = serve_in_background(**server, 1, serve_error); auto replay_response = transact(socket_path, request); server_thread.join(); REQUIRE(!serve_error.has_value()); REQUIRE(replay_response.has_value()); auto replay_success = decode_success(*replay_response); REQUIRE(replay_success.has_value()); REQUIRE(replay_success->sequence == success->sequence); REQUIRE(replay_success->intent_digest == success->intent_digest); REQUIRE(fixture->registry->record_count() == count_after_enqueue); serve_error.reset(); server_thread = serve_in_background(**server, 1, serve_error); auto conflict_response = transact( socket_path, make_enqueue_request( channel_token, "intent-1", "guest-capability-inventory", std::string(64, 'e') ) ); server_thread.join(); REQUIRE(!serve_error.has_value()); REQUIRE(conflict_response.has_value()); auto conflict_error = decode_error(*conflict_response); REQUIRE(conflict_error.has_value()); REQUIRE(conflict_error->code == "idempotency_conflict"); REQUIRE(response_is_redacted(*conflict_response)); REQUIRE(fixture->registry->record_count() == count_after_enqueue); const auto count_before_bad_token = fixture->registry->record_count(); serve_error.reset(); server_thread = serve_in_background(**server, 1, serve_error); auto bad_token_response = transact(socket_path, make_enqueue_request(std::string(64, 'f'), "intent-2")); server_thread.join(); REQUIRE(!serve_error.has_value()); REQUIRE(bad_token_response.has_value()); auto bad_token_error = decode_error(*bad_token_response); REQUIRE(bad_token_error.has_value()); REQUIRE(bad_token_error->code == "unauthorized"); REQUIRE(fixture->registry->record_count() == count_before_bad_token); serve_error.reset(); server_thread = serve_in_background(**server, 1, serve_error); auto unknown_field_response = transact( socket_path, make_enqueue_request( channel_token, "intent-3", "guest-capability-inventory", std::string(64, 'b'), 4, "issued_at_ms" ) ); server_thread.join(); REQUIRE(!serve_error.has_value()); REQUIRE(unknown_field_response.has_value()); auto unknown_field_error = decode_error(*unknown_field_response); REQUIRE(unknown_field_error.has_value()); REQUIRE(unknown_field_error->code == "invalid_request"); serve_error.reset(); server_thread = serve_in_background(**server, 1, serve_error); auto missing_body_response = transact( socket_path, std::string{"{\"schema_version\":1,\"channel_token\":\""} + std::string{channel_token} + "\"}" ); server_thread.join(); REQUIRE(!serve_error.has_value()); REQUIRE(missing_body_response.has_value()); auto missing_body_error = decode_error(*missing_body_response); REQUIRE(missing_body_error.has_value()); REQUIRE(missing_body_error->code == "invalid_request"); serve_error.reset(); server_thread = serve_in_background(**server, 1, serve_error); const std::string oversized(glove::control::max_observation_frame_bytes + 1U, 'x'); auto oversized_response = transact( socket_path, std::string{"{\"schema_version\":1,\"channel_token\":\""} + std::string{channel_token} + "\",\"body\":{\"schema\":\"sage.glove-observation.v1\",\"intent_id\":\"intent-4\"," "\"observation\":\"" + oversized + "\",\"value_digest\":\"" + std::string(64, 'c') + "\",\"item_count\":1}}" ); server_thread.join(); REQUIRE(!serve_error.has_value()); REQUIRE(oversized_response.has_value()); auto oversized_error = decode_error(*oversized_response); REQUIRE(oversized_error.has_value()); REQUIRE(oversized_error->code == "invalid_request"); const auto count_before_resilience = fixture->registry->record_count(); serve_error.reset(); server_thread = serve_in_background(**server, 1, serve_error); { auto truncated_descriptor = connect_to(socket_path); REQUIRE(truncated_descriptor.get() >= 0); const std::uint32_t declared = htonl(128U); REQUIRE(write_exact(truncated_descriptor.get(), &declared, sizeof(declared))); REQUIRE(write_exact(truncated_descriptor.get(), "{\"schema_version\":1", 18U)); } server_thread.join(); REQUIRE(!serve_error.has_value()); serve_error.reset(); server_thread = serve_in_background(**server, 1, serve_error); auto resilience_after_truncation = transact( socket_path, make_enqueue_request( channel_token, "intent-resilience-truncated", "guest-capability-inventory", std::string(64, '7') ) ); server_thread.join(); REQUIRE(!serve_error.has_value()); REQUIRE(resilience_after_truncation.has_value()); auto resilience_truncated_success = decode_success(*resilience_after_truncation); REQUIRE(resilience_truncated_success.has_value()); REQUIRE(resilience_truncated_success->status == "queued"); REQUIRE(fixture->registry->record_count() > count_before_resilience); serve_error.reset(); server_thread = serve_in_background(**server, 1, serve_error); { auto stalled = connect_to(socket_path); REQUIRE(stalled.get() >= 0); } server_thread.join(); REQUIRE(!serve_error.has_value()); serve_error.reset(); server_thread = serve_in_background(**server, 1, serve_error); auto resilience_after_disconnect = transact( socket_path, make_enqueue_request( channel_token, "intent-resilience-disconnect", "guest-capability-inventory", std::string(64, '6') ) ); server_thread.join(); REQUIRE(!serve_error.has_value()); REQUIRE(resilience_after_disconnect.has_value()); auto resilience_disconnect_success = decode_success(*resilience_after_disconnect); REQUIRE(resilience_disconnect_success.has_value()); REQUIRE(resilience_disconnect_success->status == "queued"); REQUIRE(fixture->registry->record_count() > count_before_resilience + 1U); server->reset(); REQUIRE(::access(socket_path.c_str(), F_OK) != 0); REQUIRE(::chmod(fixture->temp.root().c_str(), 0755) == 0); REQUIRE( !glove::control::observation_intent_unix_server::create( server_config_for(*fixture, fixture->temp.root() / "unsafe.sock", start_ms + 120'000U) ) .has_value() ); REQUIRE(::chmod(fixture->temp.root().c_str(), 0700) == 0); REQUIRE(write_owner_only(socket_path, "not-a-socket")); REQUIRE(!glove::control::observation_intent_unix_server::create( server_config_for(*fixture, socket_path, start_ms + 120'000U) ) .has_value()); std::filesystem::remove(socket_path); auto mismatched_config = server_config_for(*fixture, fixture->temp.root() / "mismatch.sock", start_ms + 120'000U); mismatched_config.profile_digest = std::string(64, 'f'); auto mismatch_server = glove::control::observation_intent_unix_server::create(mismatched_config); REQUIRE(mismatch_server.has_value()); const auto count_before_mismatch = fixture->registry->record_count(); serve_error.reset(); server_thread = serve_in_background(**mismatch_server, 1, serve_error); auto mismatch_response = transact( mismatch_server->get()->socket_path(), make_enqueue_request( channel_token, "intent-mismatch", "guest-capability-inventory", std::string(64, '9') ) ); server_thread.join(); REQUIRE(!serve_error.has_value()); REQUIRE(mismatch_response.has_value()); auto mismatch_error = decode_error(*mismatch_response); REQUIRE(mismatch_error.has_value()); REQUIRE(mismatch_error->code == "intent_rejected"); REQUIRE(fixture->registry->record_count() == count_before_mismatch); auto expired_config = server_config_for(*fixture, fixture->temp.root() / "expired.sock", start_ms - 1U); auto expired_server = glove::control::observation_intent_unix_server::create(expired_config); REQUIRE(expired_server.has_value()); serve_error.reset(); server_thread = serve_in_background(**expired_server, 1, serve_error); auto expired_response = transact( expired_server->get()->socket_path(), make_enqueue_request( channel_token, "intent-expired", "guest-capability-inventory", std::string(64, '8') ) ); server_thread.join(); REQUIRE(!serve_error.has_value()); REQUIRE(expired_response.has_value()); auto expired_error = decode_error(*expired_response); REQUIRE(expired_error.has_value()); REQUIRE(expired_error->code == "intent_expired"); // F3: peer-credential enforcement. A server configured with an expected // uid that cannot match the (same-uid) test client rejects connections // before any frame is read, and the registry stays untouched. { auto guarded_config = server_config_for(*fixture, fixture->temp.root() / "guarded.sock", start_ms + 120'000U); guarded_config.expected_peer_uid = ::geteuid() + 1U; auto guarded_server = glove::control::observation_intent_unix_server::create(guarded_config); REQUIRE(guarded_server.has_value()); const auto count_before_guard = fixture->registry->record_count(); serve_error.reset(); server_thread = serve_in_background(**guarded_server, 1, serve_error); auto guarded_response = transact( guarded_server->get()->socket_path(), make_enqueue_request(channel_token, "intent-guarded") ); server_thread.join(); REQUIRE(!serve_error.has_value()); // The peer is rejected without a response frame. REQUIRE(!guarded_response.has_value()); REQUIRE(fixture->registry->record_count() == count_before_guard); } // F4: replay-cache eviction (LRU) keeps the channel alive past the // cache capacity instead of returning capacity_exhausted forever. { auto bounded_config = server_config_for(*fixture, fixture->temp.root() / "bounded.sock", start_ms + 120'000U); bounded_config.replay_cache_capacity = 2U; auto bounded_server = glove::control::observation_intent_unix_server::create(bounded_config); REQUIRE(bounded_server.has_value()); const auto bounded_socket = bounded_server->get()->socket_path(); const auto count_before_lru = fixture->registry->record_count(); // One serve iteration per transact below. serve_error.reset(); server_thread = serve_in_background(**bounded_server, 6, serve_error); auto lru_first = decode_success(*transact(bounded_socket, make_enqueue_request(channel_token, "lru-1"))); REQUIRE(lru_first.has_value()); auto lru_second = decode_success(*transact(bounded_socket, make_enqueue_request(channel_token, "lru-2"))); REQUIRE(lru_second.has_value()); auto lru_third = decode_success(*transact(bounded_socket, make_enqueue_request(channel_token, "lru-3"))); REQUIRE(lru_third.has_value()); // The cached tail still replays through the cache. auto lru_cached_replay = transact(bounded_socket, make_enqueue_request(channel_token, "lru-3")); auto cached_replay = decode_success(*lru_cached_replay); REQUIRE(cached_replay.has_value()); REQUIRE(cached_replay->sequence == lru_third->sequence); // The evicted head replays through the durable registry: either an // exact replay (same host-stamped millisecond) or a drifted // idempotency_conflict — never capacity_exhausted, and the channel // stays live. auto lru_evicted_replay = transact(bounded_socket, make_enqueue_request(channel_token, "lru-1")); auto evicted_success = decode_success(*lru_evicted_replay); auto evicted_error = decode_error(*lru_evicted_replay); REQUIRE( evicted_success.has_value() || (evicted_error.has_value() && evicted_error->code == "idempotency_conflict") ); // New intents keep flowing past the old 1024-cap behavior. auto lru_fourth = transact(bounded_socket, make_enqueue_request(channel_token, "lru-4")); REQUIRE(decode_success(*lru_fourth).has_value()); server_thread.join(); REQUIRE(!serve_error.has_value()); REQUIRE(fixture->registry->record_count() == count_before_lru + 4U); } // F5: a transient accept failure (real fd exhaustion) is retried with a // bounded backoff and must not permanently kill the channel. { auto retry_config = server_config_for(*fixture, fixture->temp.root() / "retry.sock", start_ms + 120'000U); auto retry_server = glove::control::observation_intent_unix_server::create(retry_config); REQUIRE(retry_server.has_value()); const auto retry_socket = retry_server->get()->socket_path(); auto held = connect_to(retry_socket); REQUIRE(held.get() >= 0); const auto held_request = make_enqueue_request(channel_token, "intent-retry"); const auto held_size = htonl(static_cast(held_request.size())); REQUIRE(write_exact(held.get(), &held_size, sizeof(held_size))); REQUIRE(write_exact(held.get(), held_request.data(), held_request.size())); // Genuinely exhaust descriptors so the next accept() fails with // EMFILE where the platform enforces it at allocation time. std::vector exhausted; bool exhausted_descriptors = false; for (;;) { const int descriptor = ::dup(0); if (descriptor < 0) { exhausted_descriptors = errno == EMFILE; break; } exhausted.push_back(descriptor); if (exhausted.size() > 4096U) { break; } } if (exhausted_descriptors) { REQUIRE(!(*retry_server)->serve_one_for(2'000).has_value()); for (const int descriptor : exhausted) { ::close(descriptor); } // The channel recovers: both the queued connection and the new // recovery request are accepted and processed. const auto count_before_retry = fixture->registry->record_count(); serve_error.reset(); server_thread = serve_in_background(**retry_server, 2, serve_error); auto retry_response = transact( retry_socket, make_enqueue_request(channel_token, "intent-retry-recovered") ); server_thread.join(); REQUIRE(!serve_error.has_value()); REQUIRE(retry_response.has_value()); REQUIRE(decode_success(*retry_response).has_value()); REQUIRE(fixture->registry->record_count() == count_before_retry + 2U); } else { // macOS keeps new allocations below its high-water mark, so the // accept may simply succeed; the channel must still stay healthy // and process both the queued and a fresh request. for (const int descriptor : exhausted) { ::close(descriptor); } const auto count_before_retry = fixture->registry->record_count(); serve_error.reset(); server_thread = serve_in_background(**retry_server, 2, serve_error); auto retry_response = transact( retry_socket, make_enqueue_request(channel_token, "intent-retry-recovered") ); server_thread.join(); REQUIRE(!serve_error.has_value()); REQUIRE(retry_response.has_value()); REQUIRE(decode_success(*retry_response).has_value()); REQUIRE(fixture->registry->record_count() >= count_before_retry + 1U); } } auto cleanup_config = server_config_for(*fixture, fixture->temp.root() / "cleanup.sock", start_ms + 120'000U); auto cleanup_server = glove::control::observation_intent_unix_server::create(cleanup_config); REQUIRE(cleanup_server.has_value()); struct stat created_socket{}; REQUIRE(::lstat(cleanup_config.socket_path.c_str(), &created_socket) == 0); const auto created_inode = created_socket.st_ino; REQUIRE(::unlink(cleanup_config.socket_path.c_str()) == 0); REQUIRE(write_owner_only(cleanup_config.socket_path, "replacement")); struct stat replacement{}; REQUIRE(::lstat(cleanup_config.socket_path.c_str(), &replacement) == 0); REQUIRE(replacement.st_ino != created_inode); cleanup_server->reset(); REQUIRE(std::filesystem::exists(cleanup_config.socket_path)); REQUIRE(std::filesystem::file_size(cleanup_config.socket_path) > 0U); return 0; } } // namespace auto main() -> int { return run(); }