Skip to main content

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.

Schema format

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

TypeJSON SchemaExample ValuesNotes
String"type": "string""hello", ""Any valid JSON string
Integer"type": "integer"42, -10, 0Whole numbers only
Float"type": "number"3.14, 5, -2.5Accepts integers and decimals
Boolean"type": "boolean"true, falseBoolean 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:
  1. Required fields present: version, type, properties
  2. No extra properties: Only allowed fields in schema definition
  3. Default type matching: Default value must match declared type
  4. Array items defined: Arrays must have items field
  5. 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:
  1. Unknown options rejected: additionalProperties: false is auto-injected (lib.rs:368-371)
  2. Type mismatches fail: String where integer expected → Error
  3. Null values rejected: Use defaults instead, don’t set to null
  4. 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:
ChangeAllowedReason
Add new optionsBackward compatible - old code ignores new options
Add new namespacesNo impact on existing namespaces
Remove optionsWould break existing code reading that option
Remove namespacesWould break code using that namespace
Change option typesWould break type assumptions in code
Change default valuesCould change behavior unexpectedly
Rename optionsEquivalent 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"
    }
  }
}

Build docs developers (and LLMs) love