Overview
Schemas are JSON files that define available options with their types, defaults, and descriptions. They serve as the source of truth for configuration and enable runtime validation without code generation.
Schemas are stored in service repositories and baked into Docker images, ensuring the client library can validate values and provide defaults even before any ConfigMap is deployed.
Each namespace has a schema.json file with the following structure:
{
"version": "1.0",
"type": "object",
"properties": {
"system.url-prefix": {
"type": "string",
"default": "https://sentry.io",
"description": "Base URL for the Sentry instance"
},
"traces.sample-rate": {
"type": "number",
"default": 0.0,
"description": "Sample rate for traces (0.0-1.0)"
},
"feature.enabled-orgs": {
"type": "array",
"items": {"type": "string"},
"default": ["getsentry"],
"description": "Which orgs to enable the feature for"
}
}
}
Required top-level fields
Each schema file must have:
version - Semver string (e.g., "1.0", "2.1")
type - Must be "object" (top level is always an object)
properties - Map of option definitions
Required property fields
Each property in properties must have:
type - One of: string, integer, number, boolean, or array
default - Default value (must match declared type)
description - Human-readable description
items - Required if type is array. Defines the array element type.
Extra fields in properties are not allowed. The schema will fail validation if you include fields like "minimum", "maximum", "pattern", etc. Only type, default, description, and items (for arrays) are permitted.
Type system
Supported types
| Type | JSON Schema | Example Values | Notes |
|---|
| String | "type": "string" | "hello", "" | Any valid JSON string |
| Integer | "type": "integer" | 42, -10, 0 | Whole numbers only |
| Float | "type": "number" | 3.14, 5, -2.5 | Accepts integers and decimals |
| Boolean | "type": "boolean" | true, false | Boolean values |
| Array | "type": "array" | [1,2,3], ["a","b"] | Homogeneous arrays |
Array types
Arrays require an items field specifying the element type:
{
"enabled-slugs": {
"type": "array",
"items": {"type": "string"},
"default": ["getsentry", "sentry"],
"description": "List of enabled organization slugs"
}
}
Supported array element types:
string - Array of strings
integer - Array of integers
number - Array of numbers
boolean - Array of booleans
Nested arrays (array of arrays) and object types are not yet supported. Support for TypedDicts and nested arrays is planned for the future.
Number type handling
JSON Schema distinguishes between integer and number:
-
integer type:
- Accepts:
5, 42, -10
- Rejects:
5.5, 3.14 (validation error)
-
number type:
- Accepts:
5, 5.5, 3.14, -2.5
- More permissive, allows both integers and decimals
Use integer when you need whole numbers (e.g., counts, IDs). Use number for rates, percentages, or any value that might have decimals.
Validation rules
Schema validation
Schemas are validated against a meta-schema (namespace-schema.json) that enforces:
- Required fields present:
version, type, properties
- No extra properties: Only allowed fields in schema definition
- Default type matching: Default value must match declared type
- Array items defined: Arrays must have
items field
- Valid descriptions: Every property must have a description
Implementation: See lib.rs:318-339 for meta-schema validation
Default value validation
Default values are validated against their property schema using jsonschema:
// From lib.rs:342-360
fn validate_default_type(
property_name: &str,
property_schema: &Value,
default_value: &Value,
path: &Path,
) -> ValidationResult<()> {
jsonschema::validate(property_schema, default_value).map_err(|e| {
ValidationError::SchemaError {
file: path.to_path_buf(),
message: format!(
"Property '{}': default value does not match schema: {}",
property_name, e
),
}
})?;
Ok(())
}
This ensures:
- Integer defaults are whole numbers
- Boolean defaults are
true or false
- Array defaults have correct element types
- String defaults are valid strings
Value validation
Runtime values are validated against schemas with strict rules:
- Unknown options rejected:
additionalProperties: false is auto-injected (lib.rs:368-371)
- Type mismatches fail: String where integer expected → Error
- Null values rejected: Use defaults instead, don’t set to
null
- Empty values allowed: Empty strings, empty arrays are valid if type matches
Implementation: See lib.rs:189-209 for value validation
Namespace naming
Namespace directory names must follow Kubernetes naming rules:
- Allowed characters: lowercase alphanumeric,
-, .
- Must start and end: with alphanumeric character
- No uppercase:
MyService is rejected
- No underscores:
my_service is rejected
Validation: lib.rs:140-164
For service repos, namespace must be either:
{repo} (exact match, e.g., seer)
{repo}-* (prefixed, e.g., seer-autofix, seer-grouping)
Example: In the seer repo, valid namespaces are seer, seer-autofix, seer-grouping. Invalid: autofix alone.
Schema evolution rules
The validate-schema GitHub action enforces backward compatibility rules:
| Change | Allowed | Reason |
|---|
| Add new options | ✅ | Backward compatible - old code ignores new options |
| Add new namespaces | ✅ | No impact on existing namespaces |
| Remove options | ❌ | Would break existing code reading that option |
| Remove namespaces | ❌ | Would break code using that namespace |
| Change option types | ❌ | Would break type assumptions in code |
| Change default values | ❌ | Could change behavior unexpectedly |
| Rename options | ❌ | Equivalent to remove + add |
Breaking changes require a migration strategy. Contact DevInfra team before attempting schema changes that violate these rules.
Schema location and loading
In service repositories
Schemas are stored in the service repository:
seer/
└── sentry-options/
└── schemas/
├── seer/
│ └── schema.json
└── seer-autofix/
└── schema.json
They are baked into Docker images during build:
COPY sentry-options/schemas /etc/sentry-options/schemas
ENV SENTRY_OPTIONS_DIR=/etc/sentry-options
In sentry-options-automator
The repos.json file tracks schema sources:
{
"repos": {
"seer": {
"url": "https://github.com/getsentry/seer",
"path": "sentry-options/",
"sha": "abc123def456..."
}
}
}
The CLI fetches schemas from these repos for validation:
sentry-options-cli fetch-schemas --config repos.json --out schemas/
Loading at runtime
The client libraries load all schemas from the schemas directory:
// From lib.rs:240-303 (simplified)
pub fn from_directory(schemas_dir: &Path) -> ValidationResult<Self> {
for entry in fs::read_dir(schemas_dir)? {
let namespace = entry.file_name().into_string()?;
let schema_file = entry.path().join("schema.json");
let schema = Self::load_schema(&schema_file, &namespace)?;
schemas.insert(namespace, schema);
}
Ok(Self { schemas })
}
Schemas are compiled into jsonschema validators once at startup for efficient runtime validation.
Example schema
Here’s a complete example showing all supported types:
{
"version": "1.0",
"type": "object",
"properties": {
"feature.enabled": {
"type": "boolean",
"default": false,
"description": "Enable the feature"
},
"feature.rate-limit": {
"type": "integer",
"default": 100,
"description": "Rate limit per second"
},
"feature.sample-rate": {
"type": "number",
"default": 0.1,
"description": "Sampling rate (0.0-1.0)"
},
"feature.api-endpoint": {
"type": "string",
"default": "https://api.example.com",
"description": "API endpoint URL"
},
"feature.enabled-orgs": {
"type": "array",
"items": {"type": "string"},
"default": ["getsentry"],
"description": "Organizations with feature enabled"
},
"feature.retry-delays": {
"type": "array",
"items": {"type": "integer"},
"default": [1, 2, 5, 10],
"description": "Retry delay sequence in seconds"
}
}
}