glove/tests/control/receipt_audit_protocol_test.cpp
trvon afecee2c90
feat(remote): add construction-only backend
Co-authored-by: trvon <git@trevon.dev>
Signed-off-by: trvon <git@trevon.dev>
2026-08-07 22:27:35 -04:00

1192 lines
50 KiB
C++

#include "glove/container/digest.hpp"
#include "glove/container/receipt_producer.hpp"
#include "glove/control/receipt_audit_protocol.hpp"
#include "glove/supervisor/path_alias.hpp"
#include "glove/supervisor/path_exposure.hpp"
#include "glove/supervisor/session_plan.hpp"
#include "receipt_audit_wire.hpp"
#include "remote_session_runtime.hpp"
#include <glaze/glaze.hpp>
#include <sys/stat.h>
#include <unistd.h>
#include <array>
#include <cstdint>
#include <cstdio>
#include <filesystem>
#include <fstream>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <system_error>
#include <vector>
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 bootstrap_secret =
"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd";
constexpr std::string_view plan_digest =
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
class temporary_directory {
public:
temporary_directory() {
std::string pattern = "/tmp/glove-receipt-protocol-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() {
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_;
};
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 << '\n';
output.flush();
return output.good() && ::chmod(path.c_str(), 0600) == 0;
}
auto receipt() -> glove::container::resource_enforcement_receipt {
using namespace glove::container;
return {
.schema_version = 1,
.profile_digest = std::string(64, 'c'),
.backend = sandbox_backend::linux_production,
.backend_id = "linux-production:cgroup-v2-v1",
.configured_limits =
{
.cpu_time_ms = 60'000,
.memory_bytes = std::uint64_t{512} * 1024U * 1024U,
.pids = 128,
.wall_time_ms = 120'000,
.disk_bytes = std::uint64_t{1024} * 1024U * 1024U,
.terminal_output_bytes = std::uint64_t{16} * 1024U * 1024U,
},
.mechanisms =
{
.cpu_time = enforcement_mechanism::cgroup_v2,
.memory = enforcement_mechanism::cgroup_v2,
.pids = enforcement_mechanism::cgroup_v2,
.wall_time = enforcement_mechanism::watchdog,
.disk = enforcement_mechanism::filesystem_quota,
.terminal_output = enforcement_mechanism::byte_counter,
.receipt_schema_version = 1,
},
.observed =
{
.cpu_time_ms = 500,
.peak_memory_bytes = std::uint64_t{16} * 1024U * 1024U,
.peak_pids = 2,
.wall_time_ms = 750,
.disk_bytes = 4096,
.terminal_output_bytes = 1024,
},
.termination_cause = resource_termination_cause::exited,
.exit_code = 0,
.started_at_ms = 1'000,
.finished_at_ms = 1'750,
.library_projections = {},
.retained_changes = {},
};
}
auto refinement_receipt() -> glove::container::refinement_evaluation_receipt {
using namespace glove::container;
refinement_outcome outcome{
.schema = std::string{refinement_outcome_schema},
.encoding = std::string{refinement_outcome_encoding},
.metrics = {{"failed_assertions", 0}, {"latency_us", 750'000}, {"passed", 1}},
};
const auto outcome_bytes = canonical_refinement_outcome_bytes(outcome).value();
const auto outcome_digest = sha256_hex(
std::span<const unsigned char>{
reinterpret_cast<const unsigned char*>(outcome_bytes.data()), outcome_bytes.size()
}
);
return {
.schema_version = refinement_evaluation_receipt_schema_version,
.runtime_template_id = std::string{refinement_runtime_template_id},
.resource_receipt = receipt(),
.evidence_status = refinement_evidence_status::valid_outcome,
.variant = refinement_variant::candidate,
.fixture = {"fixture", std::string(64, 'f'), "fixtures"},
.base = {"base", std::string(64, 'b'), "skills"},
.candidate = {"candidate", std::string(64, 'c'), "skills"},
.matched_context_digest = std::string(64, 'd'),
.outcome =
{
.schema = outcome.schema,
.encoding = outcome.encoding,
.digest = *outcome_digest,
.byte_length = outcome_bytes.size(),
},
.evaluated_outcome = outcome,
.transcript =
{
.schema = std::string{raw_pty_transcript_schema},
.digest = std::string(64, 'e'),
.byte_count = 1'024,
.complete = true,
},
.evaluator = {
.schema = std::string{refinement_evaluator_schema},
.fixture_complete = true,
.transcript_utf8 = true,
.required_literals = 1,
.forbidden_literals = 1,
},
};
}
} // namespace
namespace wire_test {
struct rpc_error {
std::string code;
std::string message;
};
struct rpc_response {
std::string jsonrpc;
std::string id;
std::optional<glz::raw_json> result;
std::optional<rpc_error> error;
};
struct page_result {
std::uint8_t schema_version = 0;
std::vector<glove::container::authenticated_resource_enforcement_receipt> envelopes;
std::vector<glove::container::authenticated_refinement_evaluation_receipt> refinement_envelopes;
bool has_more = false;
glove::container::receipt_audit_anchor local_anchor;
};
struct acknowledgement_result {
std::uint8_t schema_version = 0;
glove::container::receipt_audit_anchor acknowledged_anchor;
};
struct receipt_audit_capabilities {
std::uint8_t envelope_schema_version = 0;
std::string algorithm;
std::string key_id;
};
struct session_control_capabilities {
bool validate_plan = true;
bool create_session = true;
bool start_session = true;
bool session_status = true;
bool attach = true;
bool resize = true;
bool write_stdin = true;
bool signal = true;
bool detach = true;
bool stop_session = true;
bool cleanup_session = true;
};
struct resource_enforcement_capabilities {
std::string cpu_time;
std::string memory;
std::string pids;
std::string wall_time;
std::string disk;
std::string terminal_output;
std::uint8_t receipt_schema_version = 1;
};
struct backend_capabilities {
std::string backend;
resource_enforcement_capabilities resource_enforcement;
};
struct supervisor_capabilities {
std::uint8_t schema_version = 0;
receipt_audit_capabilities receipt_audit;
session_control_capabilities session_control;
std::uint8_t agent_runtime_adapter_schema_version = 0;
std::vector<std::string> managed_runtime_ids;
std::uint8_t path_exposure_admin_schema_version = 0;
std::uint8_t path_exposure_catalog_schema_version = 0;
std::uint8_t retained_write_schema_version = 0;
std::uint8_t change_manifest_schema_version = 0;
std::uint8_t change_apply_authorization_schema_version = 0;
std::uint8_t refinement_evaluation_protocol_schema_version = 0;
std::vector<backend_capabilities> backends;
};
struct supervisor_health {
std::uint8_t schema_version = 0;
std::string status;
};
struct path_exposure_mode {
std::string access;
std::string materialization;
std::uint64_t max_bytes = 0;
std::string cleanup_policy;
};
struct path_exposure_projection {
std::uint8_t schema_version = 0;
std::string exposure_id;
std::uint64_t generation = 0;
std::string scope_digest;
std::string display_label;
std::vector<path_exposure_mode> allowed_modes;
std::uint64_t expires_at_ms = 0;
std::vector<std::string> allowed_runtime_template_ids;
std::string state;
};
struct path_exposure_result {
std::uint8_t schema_version = 0;
path_exposure_projection exposure;
};
struct path_exposure_list_result {
std::uint8_t schema_version = 0;
std::vector<path_exposure_projection> exposures;
};
struct session_plan_validation {
std::uint8_t schema_version = 0;
std::uint64_t policy_revision = 0;
};
struct session_record_result {
std::uint8_t schema_version = 0;
std::string session_id;
std::string controller_plan_digest;
std::string plan_content_digest;
std::string state;
std::uint64_t policy_revision = 0;
std::uint64_t expires_at_ms = 0;
std::uint64_t created_at_ms = 0;
std::optional<std::string> profile_digest;
};
} // namespace wire_test
namespace {
using wire_test::acknowledgement_result;
using wire_test::page_result;
using wire_test::rpc_error;
using wire_test::rpc_response;
using wire_test::supervisor_capabilities;
using wire_test::supervisor_health;
auto valid_plan() -> std::string {
return R"({"schema_version":1,"runtime_id":"codex","runtime_template_id":"codex-safe","adapter_command_digest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","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":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","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":61000})";
}
auto plan_validator_for(const std::filesystem::path& source)
-> glove::supervisor::result<glove::supervisor::session_plan_validator> {
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 = std::string(64, 'a'),
.backend = sandbox_backend::linux_production,
.allowed_path_aliases = {"workspace"},
.allowed_projection_destinations = {"libraries"},
.launch = {},
},
},
.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 decode_response(std::string_view frame) -> std::optional<rpc_response> {
rpc_response response;
constexpr glz::opts strict{.error_on_unknown_keys = true};
if (glz::read<strict>(response, frame)) {
return std::nullopt;
}
return response;
}
auto make_request(
std::string_view id,
std::string_view method,
std::string_view secret,
std::string_view payload,
std::optional<std::string_view> idempotency_key = std::nullopt,
std::uint64_t deadline_ms = 2'000
) -> std::string {
std::string request = "{\"jsonrpc\":\"2.0\",\"id\":\"" + std::string{id} + "\",\"method\":\"" +
std::string{method} +
"\",\"params\":{\"schema_version\":1,\"bootstrap_secret\":\"" +
std::string{secret} + "\",\"deadline_ms\":" + std::to_string(deadline_ms);
if (idempotency_key) {
request += ",\"idempotency_key\":\"" + std::string{*idempotency_key} + "\"";
}
request += ",\"payload\":" + std::string{payload} + "}}";
return request;
}
auto run() -> int {
temporary_directory temp;
REQUIRE(!temp.root().empty());
const auto key_path = temp.root() / "audit.key";
const auto journal_path = temp.root() / "receipts.journal";
REQUIRE(write_owner_only(key_path, audit_key));
const glove::container::receipt_audit_producer_config config{
.key_path = key_path,
.journal_path = journal_path,
};
auto producer = glove::container::receipt_audit_producer::initialize(config);
REQUIRE(producer.has_value());
const auto genesis = (*producer)->anchor();
REQUIRE((*producer)->acknowledge_bootstrap(genesis).has_value());
std::vector<glove::container::authenticated_resource_enforcement_receipt> terminals;
for (std::size_t index = 1; index <= 16; ++index) {
auto reservation = (*producer)->reserve_terminal();
REQUIRE(reservation.has_value());
auto terminal = (*producer)->commit_terminal(
std::move(*reservation), "session-" + std::to_string(index), plan_digest, receipt()
);
REQUIRE(terminal.has_value());
terminals.push_back(std::move(*terminal));
}
auto refinement_reservation = (*producer)->reserve_terminal();
REQUIRE(refinement_reservation.has_value());
auto refinement_terminal = (*producer)->commit_refinement_terminal(
std::move(*refinement_reservation), "session-refinement", plan_digest, refinement_receipt()
);
REQUIRE(refinement_terminal.has_value());
const auto terminal_anchor = (*producer)->anchor();
producer->reset();
auto recovered = glove::container::receipt_audit_producer::recover(config, genesis);
REQUIRE(recovered.has_value());
REQUIRE(!(*recovered)->bootstrap_reconciled());
auto protocol = glove::control::receipt_audit_protocol::create(bootstrap_secret, *recovered);
REQUIRE(protocol.has_value());
auto health_frame = (*protocol)->handle_frame(
make_request("health-1", "health", bootstrap_secret, "null"), 1'000
);
REQUIRE(health_frame.has_value());
auto health_response = decode_response(*health_frame);
REQUIRE(health_response.has_value());
REQUIRE(health_response->result.has_value());
REQUIRE(!health_response->error.has_value());
supervisor_health health;
REQUIRE(
!glz::read<glz::opts{.error_on_unknown_keys = true}>(health, health_response->result->str)
);
REQUIRE(health.schema_version == 1);
REQUIRE(health.status == "ready");
auto invalid_health = (*protocol)->handle_frame(
make_request("invalid-health", "health", bootstrap_secret, "{}"), 1'000
);
REQUIRE(invalid_health.has_value());
auto invalid_health_response = decode_response(*invalid_health);
REQUIRE(invalid_health_response.has_value());
REQUIRE(invalid_health_response->error.has_value());
REQUIRE(invalid_health_response->error->code == "invalid_request");
auto keyed_health = (*protocol)->handle_frame(
make_request(
"keyed-health", "health", bootstrap_secret, "null", "health-must-be-read-only"
),
1'000
);
REQUIRE(keyed_health.has_value());
auto keyed_health_response = decode_response(*keyed_health);
REQUIRE(keyed_health_response.has_value());
REQUIRE(keyed_health_response->error.has_value());
REQUIRE(keyed_health_response->error->code == "invalid_request");
auto capabilities_frame = (*protocol)->handle_frame(
make_request("capabilities-1", "capabilities", bootstrap_secret, "null"), 1'000
);
REQUIRE(capabilities_frame.has_value());
auto capabilities_response = decode_response(*capabilities_frame);
REQUIRE(capabilities_response.has_value());
REQUIRE(capabilities_response->result.has_value());
REQUIRE(!capabilities_response->error.has_value());
supervisor_capabilities capabilities;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(
capabilities, capabilities_response->result->str
));
REQUIRE(capabilities.schema_version == 1);
REQUIRE(capabilities.receipt_audit.envelope_schema_version == 1);
REQUIRE(capabilities.receipt_audit.algorithm == "hmac_sha256");
REQUIRE(capabilities.receipt_audit.key_id == genesis.key_id);
REQUIRE(!capabilities.session_control.validate_plan);
REQUIRE(!capabilities.session_control.create_session);
REQUIRE(!capabilities.session_control.start_session);
REQUIRE(!capabilities.session_control.session_status);
REQUIRE(!capabilities.session_control.attach);
REQUIRE(!capabilities.session_control.resize);
REQUIRE(!capabilities.session_control.write_stdin);
REQUIRE(!capabilities.session_control.signal);
REQUIRE(!capabilities.session_control.detach);
REQUIRE(!capabilities.session_control.stop_session);
REQUIRE(!capabilities.session_control.cleanup_session);
REQUIRE(capabilities.agent_runtime_adapter_schema_version == 0);
REQUIRE(capabilities.managed_runtime_ids.empty());
REQUIRE(capabilities.path_exposure_admin_schema_version == 0);
REQUIRE(capabilities.path_exposure_catalog_schema_version == 0);
REQUIRE(capabilities.retained_write_schema_version == 0);
REQUIRE(capabilities.change_manifest_schema_version == 0);
REQUIRE(capabilities.change_apply_authorization_schema_version == 0);
REQUIRE(capabilities.refinement_evaluation_protocol_schema_version == 0);
REQUIRE(capabilities.backends.size() == 2);
for (const auto& backend : capabilities.backends) {
REQUIRE(backend.resource_enforcement.cpu_time == "unavailable");
REQUIRE(backend.resource_enforcement.memory == "unavailable");
REQUIRE(backend.resource_enforcement.pids == "unavailable");
REQUIRE(backend.resource_enforcement.wall_time == "unavailable");
REQUIRE(backend.resource_enforcement.disk == "unavailable");
REQUIRE(backend.resource_enforcement.terminal_output == "unavailable");
REQUIRE(backend.resource_enforcement.receipt_schema_version == 0);
}
REQUIRE(capabilities.backends[0].backend == "linux_production");
REQUIRE(capabilities.backends[1].backend == "apple_container");
constexpr std::array<std::string_view, 11> unavailable_session_methods{
"validate_plan",
"create_session",
"start_session",
"session_status",
"attach",
"resize",
"write_stdin",
"signal",
"detach",
"stop_session",
"cleanup_session",
};
for (const auto method : unavailable_session_methods) {
auto unavailable = (*protocol)->handle_frame(
make_request(
std::string{"unavailable-"} + std::string{method},
method,
bootstrap_secret,
"null",
std::string{"unavailable-"} + std::string{method}
),
1'000
);
REQUIRE(unavailable.has_value());
auto unavailable_response = decode_response(*unavailable);
REQUIRE(unavailable_response.has_value());
REQUIRE(unavailable_response->error.has_value());
REQUIRE(unavailable_response->error->code == "method_not_found");
}
const auto exposure_root = temp.root() / "exposure-root";
const auto exposure_source = exposure_root / "sage-protocol";
REQUIRE(std::filesystem::create_directories(exposure_source));
const auto canonical_exposure_root = std::filesystem::canonical(exposure_root);
const auto canonical_exposure_source = std::filesystem::canonical(exposure_source);
auto exposure_registry = glove::supervisor::path_exposure_registry::open(
{
glove::supervisor::path_exposure_root_policy{
.root_id = "projects",
.host_root = canonical_exposure_root.string(),
.allowed_modes =
{
glove::supervisor::path_exposure_mode{
.access = glove::supervisor::path_access::read,
.materialization = glove::supervisor::path_materialization::bind,
.max_bytes = 0,
.cleanup_policy = glove::supervisor::path_cleanup_policy::retain,
},
glove::supervisor::path_exposure_mode{
.access = glove::supervisor::path_access::retained_write,
.materialization = glove::supervisor::path_materialization::copy,
.max_bytes = 67'108'864,
.cleanup_policy = glove::supervisor::path_cleanup_policy::retain,
},
},
.max_ttl_secs = 7'200,
.allowed_runtime_template_ids = {"codex-safe"},
},
},
temp.root() / "path-exposures.journal",
std::uint64_t{8} * 1024U * 1024U
);
REQUIRE(exposure_registry.has_value());
auto shared_exposures =
std::make_shared<glove::supervisor::path_exposure_registry>(std::move(*exposure_registry));
auto exposure_protocol = glove::control::receipt_audit_protocol::create(
bootstrap_secret, *recovered, {}, {}, {}, shared_exposures
);
REQUIRE(exposure_protocol.has_value());
auto exposure_capabilities =
(*exposure_protocol)
->handle_frame(
make_request("exposure-capabilities", "capabilities", bootstrap_secret, "null"),
1'000
);
REQUIRE(exposure_capabilities.has_value());
auto exposure_capabilities_response = decode_response(*exposure_capabilities);
REQUIRE(exposure_capabilities_response.has_value());
REQUIRE(exposure_capabilities_response->result.has_value());
supervisor_capabilities exposure_capability_set;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(
exposure_capability_set, exposure_capabilities_response->result->str
));
REQUIRE(exposure_capability_set.path_exposure_admin_schema_version == 1);
REQUIRE(exposure_capability_set.path_exposure_catalog_schema_version == 1);
REQUIRE(exposure_capability_set.retained_write_schema_version == 0);
REQUIRE(exposure_capability_set.change_manifest_schema_version == 0);
REQUIRE(exposure_capability_set.change_apply_authorization_schema_version == 0);
REQUIRE(exposure_capability_set.refinement_evaluation_protocol_schema_version == 0);
const auto exposure_payload =
std::string{
"{\"exposure_id\":\"sage-workspace\",\"root_id\":\"projects\",\"host_path\":\""
} +
canonical_exposure_source.string() +
"\",\"display_label\":\"Sage "
"protocol\",\"allowed_modes\":[{\"access\":\"read\",\"materialization\":\"bind\",\"max_"
"bytes\":0,\"cleanup_policy\":\"retain\"},{\"access\":\"retained_write\","
"\"materialization\":\"copy\",\"max_bytes\":33554432,\"cleanup_policy\":\"retain\"}],\"ttl_"
"secs\":3600,\"allowed_runtime_template_ids\":[\"codex-safe\"]}";
auto exposure_created = (*exposure_protocol)
->handle_frame(
make_request(
"exposure-create",
"create_path_exposure",
bootstrap_secret,
exposure_payload,
"exposure-create-1"
),
1'000
);
REQUIRE(exposure_created.has_value());
REQUIRE(exposure_created->find(canonical_exposure_source.string()) == std::string::npos);
auto exposure_created_response = decode_response(*exposure_created);
REQUIRE(exposure_created_response.has_value());
REQUIRE(exposure_created_response->result.has_value());
wire_test::path_exposure_result created_exposure;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(
created_exposure, exposure_created_response->result->str
));
REQUIRE(created_exposure.exposure.exposure_id == "sage-workspace");
REQUIRE(created_exposure.exposure.generation == 1);
REQUIRE(created_exposure.exposure.scope_digest.size() == 64U);
REQUIRE(created_exposure.exposure.state == "active");
auto exposure_listed =
(*exposure_protocol)
->handle_frame(
make_request("exposure-list", "list_path_exposures", bootstrap_secret, "null"),
2'000
);
REQUIRE(exposure_listed.has_value());
auto exposure_listed_response = decode_response(*exposure_listed);
REQUIRE(exposure_listed_response.has_value());
REQUIRE(exposure_listed_response->result.has_value());
wire_test::path_exposure_list_result listed_exposures;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(
listed_exposures, exposure_listed_response->result->str
));
REQUIRE(listed_exposures.exposures.size() == 1U);
const std::string revoke_payload = R"({"exposure_id":"sage-workspace","generation":1})";
auto exposure_revoked = (*exposure_protocol)
->handle_frame(
make_request(
"exposure-revoke",
"revoke_path_exposure",
bootstrap_secret,
revoke_payload,
"exposure-revoke-1",
4'000
),
3'000
);
REQUIRE(exposure_revoked.has_value());
auto exposure_revoked_response = decode_response(*exposure_revoked);
REQUIRE(exposure_revoked_response.has_value());
if (exposure_revoked_response->error) {
std::fprintf(
stderr,
"exposure revoke failed: %s: %s\n",
exposure_revoked_response->error->code.c_str(),
exposure_revoked_response->error->message.c_str()
);
}
REQUIRE(exposure_revoked_response->result.has_value());
wire_test::path_exposure_result revoked_exposure;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(
revoked_exposure, exposure_revoked_response->result->str
));
REQUIRE(revoked_exposure.exposure.state == "revoked");
auto revoke_replay = (*exposure_protocol)
->handle_frame(
make_request(
"exposure-revoke-replay",
"revoke_path_exposure",
bootstrap_secret,
revoke_payload,
"exposure-revoke-1",
4'000
),
3'001
);
REQUIRE(revoke_replay.has_value());
auto revoke_replay_response = decode_response(*revoke_replay);
REQUIRE(revoke_replay_response.has_value());
REQUIRE(revoke_replay_response->result.has_value());
const auto plan_source = temp.root() / "plan-source";
REQUIRE(std::filesystem::create_directory(plan_source));
std::ofstream{plan_source / "tracked.txt"} << "host-owned\n";
auto validator = plan_validator_for(plan_source);
REQUIRE(validator.has_value());
auto shared_validator =
std::make_shared<const glove::supervisor::session_plan_validator>(std::move(*validator));
auto session_registry = glove::control::session_registry::open_or_create(
temp.root() / "sessions.journal", shared_validator
);
REQUIRE(session_registry.has_value());
auto shared_sessions =
std::shared_ptr<glove::control::session_registry>{std::move(*session_registry)};
REQUIRE(!glove::control::receipt_audit_protocol::create(
bootstrap_secret, *recovered, {}, shared_sessions
)
.has_value());
auto planned_protocol = glove::control::receipt_audit_protocol::create(
bootstrap_secret, *recovered, shared_validator, shared_sessions
);
REQUIRE(planned_protocol.has_value());
auto planned_capabilities =
(*planned_protocol)
->handle_frame(
make_request("planned-capabilities", "capabilities", bootstrap_secret, "null"),
1'000
);
REQUIRE(planned_capabilities.has_value());
auto planned_capabilities_response = decode_response(*planned_capabilities);
REQUIRE(planned_capabilities_response.has_value());
REQUIRE(planned_capabilities_response->result.has_value());
supervisor_capabilities planned_capability_set;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(
planned_capability_set, planned_capabilities_response->result->str
));
REQUIRE(planned_capability_set.session_control.validate_plan);
REQUIRE(planned_capability_set.session_control.create_session);
REQUIRE(planned_capability_set.session_control.session_status);
REQUIRE(!planned_capability_set.session_control.start_session);
auto remote_runtime = glove::control::remote_session_runtime::create({
.ssh_argv = {"/usr/bin/ssh", "-F", "/tmp/glove-remote/config", "glove-remote"},
.executor_digest = "sha256:" + std::string(64U, 'a'),
.container_image = "registry.example.test/glove/runtime@sha256:" + std::string(64U, 'b'),
.container_image_digest = "sha256:" + std::string(64U, 'b'),
.channel_timeout_ms = 5'000,
.max_clock_skew_ms = 250,
.max_sessions = 4,
.staging_root = "/var/lib/glove-remote/staging",
});
REQUIRE(remote_runtime.has_value());
auto remote_control = glove::control::receipt_audit_protocol::create(
bootstrap_secret, *recovered, shared_validator, shared_sessions, *remote_runtime
);
REQUIRE(remote_control.has_value());
auto remote_capabilities =
(*remote_control)
->handle_frame(
make_request("remote-capabilities", "capabilities", bootstrap_secret, "null"), 1'000
);
REQUIRE(remote_capabilities.has_value());
auto remote_capabilities_response = decode_response(*remote_capabilities);
REQUIRE(remote_capabilities_response.has_value());
REQUIRE(remote_capabilities_response->result.has_value());
supervisor_capabilities remote_capability_set;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(
remote_capability_set, remote_capabilities_response->result->str
));
REQUIRE(remote_capability_set.session_control.create_session);
REQUIRE(remote_capability_set.session_control.session_status);
REQUIRE(!remote_capability_set.session_control.start_session);
REQUIRE(!remote_capability_set.session_control.attach);
REQUIRE(!remote_capability_set.session_control.resize);
REQUIRE(!remote_capability_set.session_control.write_stdin);
REQUIRE(!remote_capability_set.session_control.signal);
REQUIRE(!remote_capability_set.session_control.detach);
REQUIRE(!remote_capability_set.session_control.stop_session);
REQUIRE(!remote_capability_set.session_control.cleanup_session);
REQUIRE(remote_capability_set.agent_runtime_adapter_schema_version == 0);
REQUIRE(remote_capability_set.managed_runtime_ids.empty());
REQUIRE(remote_capability_set.refinement_evaluation_protocol_schema_version == 0);
REQUIRE(remote_capability_set.backends.size() == 2);
for (const auto& backend : remote_capability_set.backends) {
REQUIRE(backend.resource_enforcement.cpu_time == "unavailable");
REQUIRE(backend.resource_enforcement.receipt_schema_version == 0);
}
constexpr std::array<std::string_view, 8> remote_unavailable_methods{
"start_session",
"attach",
"resize",
"write_stdin",
"signal",
"detach",
"stop_session",
"cleanup_session",
};
for (const auto method : remote_unavailable_methods) {
auto unavailable = (*remote_control)
->handle_frame(
make_request(
std::string{"remote-unavailable-"} + std::string{method},
method,
bootstrap_secret,
"null",
"remote-unavailable"
),
1'000
);
REQUIRE(unavailable.has_value());
auto response = decode_response(*unavailable);
REQUIRE(response.has_value());
REQUIRE(response->error.has_value());
REQUIRE(response->error->code == "method_not_found");
}
auto validated =
(*planned_protocol)
->handle_frame(
make_request("validate-plan", "validate_plan", bootstrap_secret, valid_plan()),
1'000
);
REQUIRE(validated.has_value());
auto validated_response = decode_response(*validated);
REQUIRE(validated_response.has_value());
REQUIRE(validated_response->result.has_value());
wire_test::session_plan_validation validation;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(
validation, validated_response->result->str
));
REQUIRE(validation.schema_version == 1);
REQUIRE(validation.policy_revision == 7);
auto invalid_plan = (*planned_protocol)
->handle_frame(
make_request(
"invalid-plan",
"validate_plan",
bootstrap_secret,
"{\"schema_version\":1,\"argv\":[\"/bin/sh\"]}"
),
1'000
);
REQUIRE(invalid_plan.has_value());
auto invalid_plan_response = decode_response(*invalid_plan);
REQUIRE(invalid_plan_response.has_value());
REQUIRE(invalid_plan_response->error.has_value());
REQUIRE(invalid_plan_response->error->code == "invalid_plan");
auto keyed_validation =
(*planned_protocol)
->handle_frame(
make_request(
"keyed-plan", "validate_plan", bootstrap_secret, valid_plan(), "read-only-plan"
),
1'000
);
REQUIRE(keyed_validation.has_value());
auto keyed_validation_response = decode_response(*keyed_validation);
REQUIRE(keyed_validation_response.has_value());
REQUIRE(keyed_validation_response->error.has_value());
REQUIRE(keyed_validation_response->error->code == "invalid_request");
const auto create_payload = "{\"session_id\":\"session-17\",\"controller_plan_digest\":\"" +
std::string{plan_digest} + "\",\"plan\":" + valid_plan() + "}";
auto missing_create_idempotency =
(*planned_protocol)
->handle_frame(
make_request(
"create-without-key", "create_session", bootstrap_secret, create_payload
),
1'000
);
REQUIRE(missing_create_idempotency.has_value());
auto missing_create_response = decode_response(*missing_create_idempotency);
REQUIRE(missing_create_response.has_value());
REQUIRE(missing_create_response->error.has_value());
REQUIRE(missing_create_response->error->code == "invalid_request");
auto created = (*planned_protocol)
->handle_frame(
make_request(
"create-session",
"create_session",
bootstrap_secret,
create_payload,
"create-session-17"
),
1'000
);
REQUIRE(created.has_value());
auto created_response = decode_response(*created);
REQUIRE(created_response.has_value());
REQUIRE(created_response->result.has_value());
wire_test::session_record_result created_record;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(
created_record, created_response->result->str
));
REQUIRE(created_record.schema_version == 1);
REQUIRE(created_record.session_id == "session-17");
REQUIRE(created_record.controller_plan_digest == plan_digest);
REQUIRE(created_record.plan_content_digest.size() == 64);
REQUIRE(created_record.state == "created");
REQUIRE(created_record.policy_revision == 7);
auto create_replay = (*planned_protocol)
->handle_frame(
make_request(
"create-replay",
"create_session",
bootstrap_secret,
create_payload,
"create-session-17"
),
1'001
);
REQUIRE(create_replay.has_value());
auto create_replay_response = decode_response(*create_replay);
REQUIRE(create_replay_response.has_value());
REQUIRE(create_replay_response->result.has_value());
REQUIRE(create_replay_response->result->str == created_response->result->str);
auto status = (*planned_protocol)
->handle_frame(
make_request(
"status-session",
"session_status",
bootstrap_secret,
"{\"session_id\":\"session-17\"}"
),
1'001
);
REQUIRE(status.has_value());
auto status_response = decode_response(*status);
REQUIRE(status_response.has_value());
REQUIRE(status_response->result.has_value());
REQUIRE(status_response->result->str == created_response->result->str);
auto keyed_status = (*planned_protocol)
->handle_frame(
make_request(
"keyed-status",
"session_status",
bootstrap_secret,
"{\"session_id\":\"session-17\"}",
"status-is-read-only"
),
1'001
);
REQUIRE(keyed_status.has_value());
auto keyed_status_response = decode_response(*keyed_status);
REQUIRE(keyed_status_response.has_value());
REQUIRE(keyed_status_response->error.has_value());
REQUIRE(keyed_status_response->error->code == "invalid_request");
const auto genesis_json = glz::write_json(genesis);
REQUIRE(genesis_json.has_value());
const auto page_payload = "{\"sage_anchor\":" + *genesis_json + ",\"limit\":1000}";
auto page_frame = (*protocol)->handle_frame(
make_request("page-1", "verify_audit_chain", bootstrap_secret, page_payload), 1'000
);
REQUIRE(page_frame.has_value());
auto page_response = decode_response(*page_frame);
REQUIRE(page_response.has_value());
REQUIRE(page_response->result.has_value());
REQUIRE(!page_response->error.has_value());
page_result page;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(page, page_response->result->str));
REQUIRE(page.schema_version == 1);
REQUIRE(page.envelopes.size() == 15);
REQUIRE(page.envelopes.front() == terminals.front());
REQUIRE(page.envelopes.back() == terminals[14]);
REQUIRE(page.has_more);
REQUIRE(page.local_anchor == terminal_anchor);
REQUIRE(!(*recovered)->bootstrap_reconciled());
const glove::container::receipt_audit_anchor first_page_anchor{
.key_id = page.envelopes.back().key_id,
.sequence = page.envelopes.back().sequence,
.head_hmac = page.envelopes.back().this_hmac,
};
const auto first_page_anchor_json = glz::write_json(first_page_anchor);
REQUIRE(first_page_anchor_json.has_value());
const auto final_page_payload =
"{\"sage_anchor\":" + *first_page_anchor_json + ",\"limit\":1000}";
auto final_page_frame = (*protocol)->handle_frame(
make_request("page-2", "verify_audit_chain", bootstrap_secret, final_page_payload), 1'000
);
REQUIRE(final_page_frame.has_value());
auto final_page_response = decode_response(*final_page_frame);
REQUIRE(final_page_response.has_value());
REQUIRE(final_page_response->result.has_value());
page_result final_page;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(
final_page, final_page_response->result->str
));
REQUIRE(final_page.envelopes.size() == 1);
REQUIRE(final_page.envelopes.front() == terminals.back());
REQUIRE(final_page.refinement_envelopes.size() == 1);
REQUIRE(final_page.refinement_envelopes.front() == *refinement_terminal);
REQUIRE(!final_page.has_more);
REQUIRE(final_page.local_anchor == terminal_anchor);
REQUIRE(!(*recovered)->bootstrap_reconciled());
auto denied = (*protocol)->handle_frame(
make_request(
"ack-denied",
"acknowledge_audit_chain",
std::string(64, 'e'),
"{}",
"receipt-ack-denied"
),
1'000
);
REQUIRE(denied.has_value());
auto denied_response = decode_response(*denied);
REQUIRE(denied_response.has_value());
REQUIRE(denied_response->error.has_value());
REQUIRE(denied_response->error->code == "unauthorized");
REQUIRE(!(*recovered)->bootstrap_reconciled());
const auto terminal_anchor_json = glz::write_json(terminal_anchor);
REQUIRE(terminal_anchor_json.has_value());
const auto ack_payload = "{\"anchor\":" + *terminal_anchor_json + "}";
const auto ack_request = make_request(
"ack-1", "acknowledge_audit_chain", bootstrap_secret, ack_payload, "receipt-ack-1"
);
auto ack_frame = (*protocol)->handle_frame(ack_request, 1'000);
REQUIRE(ack_frame.has_value());
auto ack_response = decode_response(*ack_frame);
REQUIRE(ack_response.has_value());
REQUIRE(ack_response->result.has_value());
acknowledgement_result acknowledgement;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(
acknowledgement, ack_response->result->str
));
REQUIRE(acknowledgement.acknowledged_anchor == terminal_anchor);
REQUIRE((*recovered)->bootstrap_reconciled());
const auto replay_request = make_request(
"ack-2", "acknowledge_audit_chain", bootstrap_secret, ack_payload, "receipt-ack-1"
);
auto replay = (*protocol)->handle_frame(replay_request, 1'001);
REQUIRE(replay.has_value());
auto replay_response = decode_response(*replay);
REQUIRE(replay_response.has_value());
REQUIRE(replay_response->id == "ack-2");
REQUIRE(replay_response->result.has_value());
REQUIRE(replay_response->result->str == ack_response->result->str);
const auto conflicting_payload = "{\"anchor\":" + *genesis_json + "}";
auto conflict = (*protocol)->handle_frame(
make_request(
"ack-conflict",
"acknowledge_audit_chain",
bootstrap_secret,
conflicting_payload,
"receipt-ack-1"
),
1'001
);
REQUIRE(conflict.has_value());
auto conflict_response = decode_response(*conflict);
REQUIRE(conflict_response.has_value());
REQUIRE(conflict_response->error.has_value());
REQUIRE(conflict_response->error->code == "idempotency_conflict");
auto expired = (*protocol)->handle_frame(
make_request(
"expired", "verify_audit_chain", bootstrap_secret, page_payload, std::nullopt, 999
),
1'000
);
REQUIRE(expired.has_value());
auto expired_response = decode_response(*expired);
REQUIRE(expired_response.has_value());
REQUIRE(expired_response->error.has_value());
REQUIRE(expired_response->error->code == "deadline_exceeded");
auto fresh_config = config;
fresh_config.journal_path = temp.root() / "fresh-receipts.journal";
auto fresh_protocol =
glove::control::receipt_audit_protocol::create(bootstrap_secret, fresh_config);
REQUIRE(fresh_protocol.has_value());
auto fresh_capabilities =
(*fresh_protocol)
->handle_frame(
make_request("fresh-capabilities", "capabilities", bootstrap_secret, "null"), 1'000
);
REQUIRE(fresh_capabilities.has_value());
auto fresh_capabilities_response = decode_response(*fresh_capabilities);
REQUIRE(fresh_capabilities_response.has_value());
REQUIRE(fresh_capabilities_response->result.has_value());
REQUIRE(!std::filesystem::exists(fresh_config.journal_path));
auto invalid_capabilities = (*fresh_protocol)
->handle_frame(
make_request(
"invalid-capabilities",
"capabilities",
bootstrap_secret,
"null",
"capabilities-must-be-read-only"
),
1'000
);
REQUIRE(invalid_capabilities.has_value());
auto invalid_capabilities_response = decode_response(*invalid_capabilities);
REQUIRE(invalid_capabilities_response.has_value());
REQUIRE(invalid_capabilities_response->error.has_value());
REQUIRE(invalid_capabilities_response->error->code == "invalid_request");
REQUIRE(!std::filesystem::exists(fresh_config.journal_path));
const auto genesis_ack_payload = "{\"anchor\":" + *genesis_json + "}";
auto premature_ack = (*fresh_protocol)
->handle_frame(
make_request(
"fresh-ack-before-page",
"acknowledge_audit_chain",
bootstrap_secret,
genesis_ack_payload,
"fresh-receipt-ack"
),
1'000
);
REQUIRE(premature_ack.has_value());
auto premature_ack_response = decode_response(*premature_ack);
REQUIRE(premature_ack_response.has_value());
REQUIRE(premature_ack_response->error.has_value());
REQUIRE(premature_ack_response->error->code == "audit_reconciliation_failed");
REQUIRE(!std::filesystem::exists(fresh_config.journal_path));
auto fresh_page =
(*fresh_protocol)
->handle_frame(
make_request("fresh-page", "verify_audit_chain", bootstrap_secret, page_payload),
1'000
);
REQUIRE(fresh_page.has_value());
auto fresh_page_response = decode_response(*fresh_page);
REQUIRE(fresh_page_response.has_value());
REQUIRE(fresh_page_response->result.has_value());
page_result empty_page;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(
empty_page, fresh_page_response->result->str
));
REQUIRE(empty_page.envelopes.empty());
REQUIRE(empty_page.refinement_envelopes.empty());
REQUIRE(!empty_page.has_more);
REQUIRE(empty_page.local_anchor == genesis);
REQUIRE(std::filesystem::exists(fresh_config.journal_path));
auto fresh_ack = (*fresh_protocol)
->handle_frame(
make_request(
"fresh-ack",
"acknowledge_audit_chain",
bootstrap_secret,
genesis_ack_payload,
"fresh-receipt-ack"
),
1'000
);
REQUIRE(fresh_ack.has_value());
auto fresh_ack_response = decode_response(*fresh_ack);
REQUIRE(fresh_ack_response.has_value());
REQUIRE(fresh_ack_response->result.has_value());
return 0;
}
} // namespace
auto main() -> int {
return run();
}