Skip to content

Custom option redaction

Mark fields as sensitive in .proto, then clear them with reflection before logging or exporting a message. The same pattern works for any custom field option.

See Custom options and Dynamic messages for the underlying APIs.

Create proto/options.proto:

syntax = "proto3";
package example.options;
import "google/protobuf/descriptor.proto";
extend google.protobuf.FieldOptions {
bool sensitive = 8765;
}
syntax = "proto3";
package example;
import "options.proto";
message User {
string first_name = 1;
string last_name = 2;
string email = 3 [(example.options.sensitive) = true];
}

getOption() reads the generated extension descriptor. reflect() lets the code clear fields without hardcoding email.

import { getOption, type DescMessage, type Message } from "@bufbuild/protobuf";
import { reflect } from "@bufbuild/protobuf/reflect";
import { sensitive } from "./gen/options_pb";
export function redact(schema: DescMessage, message: Message): void {
const reflected = reflect(schema, message);
for (const field of reflected.fields) {
if (getOption(field, sensitive)) {
reflected.clear(field);
}
}
}

Call it with a generated schema:

import { create } from "@bufbuild/protobuf";
import { UserSchema } from "./gen/example_pb";
const user = create(UserSchema, {
firstName: "Lisa",
lastName: "Simpson",
email: "lisa@example.com",
});
redact(UserSchema, user);
user.email; // ""

For the reference material behind this pattern, see Custom options, Descriptors, and Dynamic messages.