schemas
Sensor Config
created 23 August 2026 · last modified 23 August 2026
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://kinetic-trust-protocol.net/specs/schemas/v2/sensor-config.json",
"title": "KTP Sensor Feed Configuration",
"description": "Configuration schema for Context Signals sensor feeds",
"type": "object",
"required": ["dimension", "feeds"],
"properties": {
"dimension": {
"type": "string",
"enum": ["evidence_density", "trust_trend", "adversarial_pressure", "moment_criticality", "update_resistance", "attestation_coverage", "soul"],
"description": "The Risk Factor this configuration applies to, or soul for the veto"
},
"feeds": {
"type": "array",
"items": {
"$ref": "#/$defs/feed"
},
"minItems": 1,
"description": "Array of sensor feeds for this Risk Factor"
},
"aggregation": {
"type": "string",
"enum": ["weighted_average", "any_veto", "all_veto", "max", "min"],
"default": "weighted_average",
"description": "How to aggregate multiple feeds (the soul veto must use any_veto)"
},
"default_on_failure": {
"type": "number",
"minimum": 0.5,
"maximum": 1,
"default": 1,
"description": "Substitute value for feeds that are unavailable or stale. This stands in for an UNKNOWN observation, not for a measurement of zero risk, so it is floored at 0.5 - the value ktp-core 5.2 glosses as moderate stress. Zero is a measurement of perfect conditions and MUST NOT be used to represent an absent one; a deployment able to declare it could raise its own trust score by disabling sensors. Required unless aggregation is any_veto."
}
},
"allOf": [
{
"if": {
"properties": { "aggregation": { "not": { "const": "any_veto" } } }
},
"then": { "required": ["default_on_failure"] }
},
{
"if": {
"properties": { "aggregation": { "const": "any_veto" } },
"required": ["aggregation"]
},
"then": {
"properties": {
"default_on_failure": {
"const": 1,
"description": "A veto-aggregated dimension has no partial state. If its feeds are unavailable, no clearance has been returned and the veto stands."
}
}
}
}
],
"$defs": {
"feed": {
"type": "object",
"required": ["id", "source", "enabled"],
"properties": {
"id": {
"type": "string",
"pattern": "^[a-z0-9-]+$",
"description": "Unique identifier for this feed within the dimension"
},
"type": {
"type": "string",
"enum": ["physical", "network", "security", "application", "governance"],
"description": "Category of sensor"
},
"source": {
"type": "string",
"description": "URI of the data source (mqtt://, https://, local://)"
},
"enabled": {
"type": "boolean",
"description": "Whether this feed is active"
},
"weight": {
"type": "number",
"minimum": 0,
"default": 1.0,
"description": "Weight of this feed in aggregation (for weighted dimensions)"
},
"normalization": {
"$ref": "#/$defs/normalization"
},
"refresh_interval_ms": {
"type": "integer",
"anyOf": [
{ "const": 0 },
{ "minimum": 100 }
],
"description": "How often to poll this source (milliseconds). 0 means the feed is not polled and is queried on demand at decision time, which is how the sovereignty feeds are configured."
},
"stale_threshold_ms": {
"type": "integer",
"anyOf": [
{ "const": 0 },
{ "minimum": 1000 }
],
"description": "How long before an observation is no longer current. Past this age the feed is UNAVAILABLE and ktp-sensors 6.2 governs; the last known value MUST NOT be substituted. 0 means no cached observation is ever current - the pairing for an on-demand feed. Where this is omitted it defaults to five times refresh_interval_ms (ktp-sensors 6.1)."
},
"veto_on_match": {
"type": "boolean",
"default": false,
"description": "For a soul feed: whether a match triggers the veto"
},
"transform": {
"type": "string",
"description": "Optional JSONPath or transformation expression"
}
}
},
"normalization": {
"type": "object",
"properties": {
"min": {
"type": "number",
"description": "Raw value that maps to 0"
},
"max": {
"type": "number",
"description": "Raw value that maps to 1"
},
"invert": {
"type": "boolean",
"default": false,
"description": "Whether to invert the scale (1 becomes 0, 0 becomes 1)"
},
"clamp": {
"type": "boolean",
"default": true,
"description": "Whether to clamp values outside min/max to 0/1"
}
}
}
},
"examples": [
{
"dimension": "evidence_density",
"feeds": [
{
"id": "co2-sensor",
"type": "physical",
"source": "mqtt://sensors.local/building/co2",
"enabled": true,
"weight": 1.0,
"normalization": {
"min": 400,
"max": 2000,
"invert": false
},
"refresh_interval_ms": 30000,
"stale_threshold_ms": 120000
},
{
"id": "badge-count",
"type": "physical",
"source": "https://access.local/api/v1/count",
"enabled": true,
"weight": 1.5,
"normalization": {
"min": 0,
"max": 5000,
"invert": false
},
"refresh_interval_ms": 60000,
"stale_threshold_ms": 300000
}
],
"aggregation": "weighted_average",
"default_on_failure": 0.8
},
{
"dimension": "soul",
"feeds": [
{
"id": "tk-labels",
"type": "governance",
"source": "https://api.localcontexts.org/v1/labels",
"enabled": true,
"veto_on_match": true,
"refresh_interval_ms": 0,
"stale_threshold_ms": 0
},
{
"id": "ocap-registry",
"type": "governance",
"source": "https://ocap.example.org/api/check",
"enabled": true,
"veto_on_match": true,
"refresh_interval_ms": 0,
"stale_threshold_ms": 0
},
{
"id": "sacred-geofence",
"type": "governance",
"source": "local://geofence-service",
"enabled": true,
"veto_on_match": true,
"refresh_interval_ms": 0,
"stale_threshold_ms": 0
}
],
"aggregation": "any_veto"
}
]
}