FLEDGE has been renamed to Protected Audience API. To learn more about the name change, see the blog post
Authors:
Shruti Agarwal, Google Privacy Sandbox
Peter Meric, Google Privacy Sandbox
Brian Schneider, Google Privacy Sandbox
Edward Gathuru, Google Privacy Sandbox
Roma is a C++ library used for executing untrusted code in a secure, isolated environment.
Currently, Roma supports ad tech-defined functions implemented in JavaScript and Web Assembly. In this document, we propose an extension to Roma to allow binary executables to be provided as UDFs.
In Privacy Sandbox, Roma is a C++ library used by Protected Audience's trusted servers to securely execute ad tech-developed functions, referred to as user-defined functions (UDFs), within a secure, isolated environment. Central to this sandboxing is the requirement that the UDF execution handles requests and associated data without any discernible side effects.
Roma's current design uses Sandbox2 and V8 as the execution engine, imposing certain limitations on ad techs that restrict them to the use of JavaScript and WebAssembly (WASM).
This explainer presents an expansion in Roma's functionality to execute self-contained binaries.
Bring-Your-Own-Binary can reduce costs to ad tech by doing the following:
- Reducing the barrier-to-entry - less code to change and time to migrate.
- Improving latency and reducing cost - our internal benchmarks have shown compute intensive UDFs implemented in C++ and Go have lower latency than equivalent ones written in WASM or JS.
Roma Bring-Your-Own-Binary uses a single instance of a double-sandboxed Virtual Machine Monitor
(VMM) called gVisor. Inside this sandbox, privacy requirements are met
through per-process isolation. This involves calling clone
with appropriate flags, creating
pivot_root
for file system isolation, executing the supplied UDF, waiting for execution to
complete and then cleaning up the UDF process and pivot_root
.
This section describes how Roma Bring-Your-Own-Binary might be used.
BYOB integration will be available through Trusted Execution Environment (TEE) based servers : Key/Value service, Bidding & Auction services. Browsers will continue to support JavaScript or WASM-based UDF execution based on V8 engine, while TEE based servers depending on Roma for adtech's UDF execution will continue to support v8.
To simplify UDF debugging and testing, the team plans to release an SDK tool to help the UDF developer determine if their binary meets the UDF requirements regardless of their development environment.
A REPL CLI tool will allow the developer to run their UDF binary in a sandboxed setting in production or debug modes, making it easier to test or debug. A microbenchmark CLI tool will also be offered for benchmarking UDF binaries.
The BYOB SDK for a given UDF contains the Protocol Buffer (protobuf) spec defining the messages pertinent to the UDF input and output. This protobuf spec defines the expected input and output for the binary.
Protobuf messages are used for requests and responses. Communications with the UDF are facilitated by protobuf messages transmitted using a file descriptor.
The specification of the UDF communication protocol can be found in doc. The following section offers an illustrative example.
The example below demonstrates how BYOB can be used. Assume you have been supplied with the following proto as the specification for your UDF.
// Echo UDF: An API which reads the request and echoes the response.
// The Echo UDF request.
// Request: EchoRequest
// Response: EchoResponse
// Request message for the Echo function.
message EchoRequest {
// Message to be echoed.
bytes message = 1;
}
// The Echo UDF response.
// Response message for the Echo function.
message EchoResponse {
// Response which would be the a copy of the message sent in EchoRequest.
bytes message = 1;
}
A UDF written in C++ might look like the following.
#include <iostream>
#include "google/protobuf/util/delimited_message_util.h"
#include "src/roma/byob/example/example.pb.h"
using ::privacy_sandbox::server_common::byob::example::EchoRequest;
using ::privacy_sandbox::server_common::byob::example::EchoResponse;
EchoRequest ReadRequestFromFd(int fd) {
EchoRequest req;
google::protobuf::io::FileInputStream stream(fd);
google::protobuf::util::ParseDelimitedFromZeroCopyStream(&req, &stream,
nullptr);
return req;
}
void WriteResponseToFd(int fd, EchoResponse resp) {
google::protobuf::util::SerializeDelimitedToFileDescriptor(resp, fd);
}
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Expecting exactly one argument";
return -1;
}
int fd = std::stoi(argv[1]);
// Any initialization work can be done before this point.
// The following line will result in a blocking read being performed by the
// binary i.e. waiting for input before execution.
// The EchoRequest proto is defined by the Trusted Server team. The UDF reads
// request from the provided file descriptor.
EchoRequest request = ReadRequestFromFd(fd);
EchoResponse response;
response.set_message(request.message());
// Once the UDF is done executing, it should write the response (EchoResponse
// in this case) to the provided file descriptor.
WriteResponseToFd(fd, std::move(response));
return 0;
}
This C++ code should be compiled to a binary and provided to the server.
For additional examples, refer to our code repository.
Note that these examples are non-exhaustive. Self-contained executables are generally supported. For details see doc.
Roma BYOB is open-sourced and the code can be found on GitHub.
For details about the execution environment and communication protocol, check out documentation.
For questions and bug reports, file an issue on GitHub.