Skip to main content
Version: v0.2.0

Configuration Validation

protoconf provides two ways to add validation rules to your configuration, and runs both at compile time:

  • Constraints declared in the .proto file, using protovalidate. Good for the rules that belong to the schema itself — ranges, required fields, string formats.
  • Starlark rules, written in files with the -validator suffix and attached with add_validator. Good for rules that need real logic — cross-field invariants, lookups, anything you would write as code.

A config has to satisfy both before it is materialized.

Constraints in the schema

New in v0.2.0

Add buf.build/bufbuild/protovalidate to your protos and declare the constraints alongside the fields they govern:

syntax = "proto3";

package myproject.v1;

import "buf/validate/validate.proto";
import "google/protobuf/duration.proto";

message ServerConfiguration {
bool is_debug = 1;
uint32 max_connections = 2 [(buf.validate.field).uint32.gte = 1];
float max_payload_size_mb = 3 [
(buf.validate.field).float.gte = 0.1,
(buf.validate.field).float.lte = 100.0
];
google.protobuf.Duration request_timeout = 4 [
(buf.validate.field).duration.gte = {seconds: 1}
];
}

protoconf compile rejects any message that violates these constraints, including messages nested inside other messages, inside repeated fields, and inside map keys and values.

Because the constraints live in the .proto file, every consumer of that schema — not just protoconf — sees the same rules.

Starlark validation rules

Validation rules are written in separate Starlark files with the -validator suffix, and they are associated with your configuration using the add_validator function.

Writing a validation rule

Create a new Starlark file with the -validator suffix. For example, for a configuration defined in ./src/myproject/v1/server_config.proto, the validation file would be ./src/myproject/v1/server_config.proto-validator.

In this file, load the configuration message, define a validation function, and then add the validator:

load("//myproject/v1/server_config.proto", "ServerConfiguration")

def validate_server_config(config):
if config.max_connections < 1:
fail("max_connections must be at least 1")

if config.max_payload_size_mb < 0.1 or config.max_payload_size_mb > 100.0:
fail("max_payload_size_mb must be between 0.1 and 100.0")

if config.request_timeout.seconds < 1:
fail("request_timeout must be at least 1 second")

add_validator(ServerConfiguration, validate_server_config)

In this example, validate_server_config is a function that checks whether max_connections is at least 1, max_payload_size_mb is between 0.1 and 100.0, and request_timeout is at least 1 second. If any of these conditions is not met, the function calls fail() with an error message.

After defining the validation function, add_validator() is called to associate the validation function with the ServerConfiguration message. Now, whenever a ServerConfiguration object is created, its values will be validated according to these rules.

With validation in place, you can ensure that your configuration values are always within acceptable ranges, preventing errors due to incorrect configurations.

When a validator fails, protoconf compile prints the Starlark backtrace that led to the failing value, so you can see which line of your configuration produced it rather than only which rule rejected it.