Documentos de Decision Gate

Evaluación de puertas determinista, reproducible con decisiones auditables.

Documentación de Asset Core

Guía de Desarrollo del Proveedor

A primera vista

Qué: Construir proveedores de evidencia personalizados para Decision Gate Por qué: Ampliar las fuentes de evidencia sin modificar el núcleo Quién: Desarrolladores de proveedores, ingenieros de integración Requisitos previos: provider_protocol.md, JSON-RPC 2.0


Arquitectura del Proveedor

Un proveedor es un servidor MCP que implementa una herramienta: evidence_query.

Provider
  - Provider Contract (JSON)
    - provider_id, name, description
    - transport = "mcp"
    - config_schema
    - checks (params schema, result schema, comparators, examples)
    - notes
  - MCP Server
    - tools/call -> evidence_query

Decision Gate
  - Loads contract from capabilities_path
  - Validates ScenarioSpec conditions against contract
  - Calls evidence_query during scenario_next

Decision Gate no utiliza tools/list en tiempo de ejecución. Implémentelo para la compatibilidad con MCP, pero mantenga el archivo de contrato como autoritativo.


Tipos de Proveedores

TipoConfiguraciónTransporteCaso de Uso
Incorporadotype = "builtin"En procesotime, env, json, http
MCP Externotype = "mcp"stdio o HTTPProveedores personalizados

Los proveedores externos de MCP están configurados con o:

  • command = ["/path/to/provider", "arg"] (stdio)
  • url = "https://provider/rpc" (HTTP)

capabilities_path es obligatorio para todos los proveedores de MCP. Los nombres de los proveedores deben ser únicos; los identificadores integrados (time, env, json, http) están reservados solo para type = "builtin".


Inicio Rápido: Proveedor Mínimo

Paso 1: Usa una plantilla de SDK

Las plantillas se encuentran en decision-gate-provider-sdk/ (Python, TypeScript, Go). Implementan:

  • Enmarcado de Content-Length (stdio)
  • tools/list y tools/call
  • Análisis de JSON-RPC

Paso 2: Implementar evidence_query

Su controlador debe devolver un objeto EvidenceResult (no un error JSON-RPC) para fallos normales.

Ejemplo (pseudocódigo):

def handle_evidence_query(query, context):
    if query["check_id"] != "file_exists":
        return {
            "value": None,
            "lane": "verified",
            "error": {
                "code": "unsupported_check",
                "message": "unknown check",
                "details": {"check_id": query["check_id"]}
            },
            "evidence_hash": None,
            "evidence_ref": None,
            "evidence_anchor": None,
            "signature": None,
            "content_type": None
        }

    rel_path = query.get("params", {}).get("path")
    if not rel_path:
        return {
            "value": None,
            "lane": "verified",
            "error": {
                "code": "params_missing",
                "message": "missing path",
                "details": {"param": "path"}
            },
            "evidence_hash": None,
            "evidence_ref": None,
            "evidence_anchor": None,
            "signature": None,
            "content_type": None
        }

    root = CONFIG["root"]
    root_id = CONFIG["root_id"]
    abs_path = os.path.normpath(os.path.join(root, rel_path))
    exists = os.path.exists(abs_path)
    return {
        "value": {"kind": "json", "value": exists},
        "lane": "verified",
        "error": None,
        "evidence_hash": None,
        "evidence_ref": {"uri": f"dg+file://{root_id}/{rel_path}"},
        "evidence_anchor": {
            "anchor_type": "file_path_rooted",
            "anchor_value": json.dumps({"root_id": root_id, "path": rel_path}, separators=(",", ":"), sort_keys=True)
        },
        "signature": None,
        "content_type": "application/json"
    }

Paso 3: Crear un Contrato de Proveedor

Todos los campos a continuación son requeridos por el esquema del contrato.

{
  "provider_id": "file-provider",
  "name": "File Provider",
  "description": "File existence checks",
  "transport": "mcp",
  "config_schema": {
    "type": "object",
    "additionalProperties": false,
    "properties": {}
  },
  "checks": [
    {
      "check_id": "file_exists",
      "description": "Check if a file exists",
      "determinism": "external",
      "params_required": true,
      "params_schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": { "path": { "type": "string" } },
        "required": ["path"]
      },
      "result_schema": { "type": "boolean" },
      "allowed_comparators": ["equals", "not_equals"],
      "anchor_types": ["file_path_rooted"],
      "content_types": ["application/json"],
      "examples": [
        {
          "description": "Check a report file",
          "params": { "path": "report.json" },
          "result": true
        }
      ]
    }
  ],
  "notes": [
    "External: depends on the local filesystem state."
  ]
}

Reglas importantes del contrato:

  • allowed_comparators debe ser no vacío y estar en orden canónico.
  • params_required debe coincidir con si params_schema requiere campos.
  • transport debe ser "mcp" para proveedores externos.

Paso 4: Configurar la Puerta de Decisión

[[providers]]
name = "file-provider"
type = "mcp"
command = ["python3", "/path/to/provider.py"]
capabilities_path = "contracts/file-provider.json"

Tiempos de espera

La puerta de decisión solo aplica tiempos de espera HTTP a HTTP MCP proveedores:

[[providers]]
name = "cloud"
type = "mcp"
url = "https://provider.example.com/rpc"
capabilities_path = "contracts/cloud.json"
timeouts = { connect_timeout_ms = 2000, request_timeout_ms = 10000 }

No hay ningún tiempo de espera para el Decision Gate para proveedores MCP de stdio; mantenga sus controladores rápidos y deterministas.


Evidencia de Firma

Cuando trust.default_policy = { require_signature = { keys = [...] } }, los proveedores deben incluir firmas:

"signature": {
  "scheme": "ed25519",
  "key_id": "/etc/decision-gate/keys/provider.pub",
  "signature": [1, 2, 3, 4]
}

Algoritmo de firma (exacto):

  1. Compute evidence_hash from the evidence value.
    • valor JSON -> bytes JSON canónicos -> sha256
    • Valor de bytes -> sha256
  2. Serializa el objeto HashDigest como JSON canónico.
  3. Firme esos bytes con Ed25519.

Decision Gate verifica que la firma coincida con el archivo de clave pública configurado. Si evidence_hash falta, DG lo calcula antes de la verificación. Si evidence_hash está presente, debe coincidir con el hash canónico del valor de evidencia o la respuesta será rechazada.


Manejo de Errores

  • Devuelve EvidenceResult.error para errores esperados.
  • Utilice errores de JSON-RPC solo para fallos de protocolo (JSON-RPC inválido, bloqueos internos). DG convierte los errores de JSON-RPC en provider_error.

Los códigos de error son definidos por el proveedor. Mantenlos estables y legibles por máquina (por ejemplo, params_missing, file_not_found).


Referencia

  • Esquema del contrato del proveedor: crates/decision-gate-contract/src/schemas.rs (provider_contract_schema)
  • Contratos de proveedores integrados: providers.json
  • Plantillas de SDK: decision-gate-provider-sdk/

Glosario

EvidenceQuery: Solicitud { provider_id, check_id, params }. EvidenceResult: Valor de respuesta del proveedor + metadatos. Provider Contract: Documento JSON que describe verificaciones, esquemas, comparadores y ejemplos. Signature: Firma Ed25519 sobre JSON canónico de evidence_hash.