Skip to main content
Glama
wfengq

artifactdiff-mcp

by wfengq

ArtifactDiff

Una puerta de verificación de cambios de contratos para humanos y agentes de IA.

Demuestra que un agente de IA cambió solo los términos del contrato de Office/PDF que autorizaste — semántica, visual y criptográficamente — antes de que el documento sea entregado.

ArtifactDiff es local-first y offline. Convierte una edición solicitada, como “cambiar el período de pago de 30 días a 45 días”, en una política congelada, verifica el candidato y escribe un Review Bundle inmutable que tanto humanos como agentes pueden inspeccionar.

Estado del proyecto: 0.1.0 alpha. El núcleo contract-safe y el Golden Path firmado están implementados y cubiertos por más de 900 pruebas automatizadas. El empaquetado y la automatización de lanzamiento público aún están en progreso.

Qué detecta

Candidate

Verdict

Delivery gate

Solo cambió la ocurrencia exacta de la cláusula autorizada

PASE

Puede proceder

La edición es semánticamente permitida pero tiene un cambio de diseño no explicado

REVISIÓN

Bloqueado hasta que un humano apruebe ese hallazgo

Una parte, cantidad, fecha, firma, sello, anexo u otro contenido protegido cambió

FALLO

Bloqueado y no aprobable

ArtifactDiff está deliberadamente diseñado para fallar de forma cerrada. La revisión sigue siendo bloqueante por defecto y un hallazgo de FALLO no puede ser aprobado.

Related MCP server: Decapod Intent Kernel

Golden Path de tres minutos

Requisitos previos: Python 3.11+ y una copia local de este repositorio. La demo usa solo contratos sintéticos y no realiza ninguna solicitud de red después de instalar las dependencias.

python -m venv .venv
# Activate .venv using your shell, then:
python -m pip install -e ".[dev]"
python scripts/run_contract_golden_path.py --output build/contract-golden-path
python -m json.tool build/contract-golden-path/summary.json

La ejecución crea tres Review Bundles firmados con Ed25519 y verificados de forma independiente:

authorized    raw=pass    effective=pass
review        raw=review  effective=review
unauthorized  raw=fail    effective=fail

El caso review permanece bloqueado. Para demostrar la transición de cadena de eventos firmada con un revisor humano sintético explícitamente simulado, usa un directorio de salida diferente:

python scripts/run_contract_golden_path.py --output build/contract-golden-path-approved --approve-review

--approve-review es solo para demo. Las aprobaciones reales siguen siendo acciones humanas interactivas a través de artifactdiff review o artifactdiff approve; los agentes no pueden aprobar sus propios hallazgos. Consulta el recorrido del Golden Path para ver los archivos generados y los límites de privacidad.

El flujo de trabajo

Human instruction
      │
      ▼
contract-safe policy ── freeze + optional Ed25519 authorization
      │
      ▼
controlled agent edit session
      │
      ▼
semantic rules + protected entities + visual envelope
      │
      ├── PASS ───────────────────────────────► deliver
      ├── REVIEW ─► signed per-finding review ─► deliver or reject
      └── FAIL ───────────────────────────────► reject
                          │
                          ▼
                 immutable Review Bundle

El Review Bundle es el sistema de registro. El escritorio de revisión local es solo una interfaz de loopback autenticada sobre ese bundle.

Interfaces humanas y de automatización

El mismo límite de aplicación impulsa todas las interfaces:

  • CLI: crear/validar/sellar políticas, abrir sesiones controladas, verificar cambios, inspeccionar hallazgos, aprobar hallazgos y verificar o empaquetar bundles.

  • Escritorio de revisión local: inspeccionar un bundle y firmar un hallazgo a la vez sin enviar contratos ni claves privadas a JavaScript del navegador.

  • MCP: herramientas acotadas de inspección/borrador/validación/verificación local para agentes de IA, con raíces de entrada y salida separadas y sin capacidad de aprobación ni firma verificada.

  • HTML offline: informes de revisión humana portátiles vinculados a hechos verificados.

artifactdiff --help
artifactdiff policy --help
artifactdiff verify --help
artifactdiff review path/to/review-bundle

Para un host MCP, configura raíces absolutas separadas por separadores de ruta antes de iniciar el servidor stdio:

ARTIFACTDIFF_MCP_INPUT_ROOTS=/absolute/contracts
ARTIFACTDIFF_MCP_OUTPUT_ROOTS=/absolute/artifactdiff-output
artifactdiff-mcp

MCP intencionalmente no puede aprobar hallazgos ni realizar firmas verificadas.

Por qué esto no es otro diff de PDF

Diff de documentos ordinario

ArtifactDiff

Muestra todo lo que cambió

Demuestra si los cambios coinciden con una instrucción preautorizada

El texto o los píxeles son el resultado final

Reglas semánticas, de entidades protegidas, de ocurrencia, de metadatos y visuales se combinan en una sola puerta

Una captura de pantalla/informe es la evidencia

Review Bundle con direccionamiento por contenido con política, hechos, veredicto, evidencia, firmas y decisiones de solo añadidura

Diseñado solo para una persona que mira dos archivos

CLI, Python, MCP, informe offline y escritorio de revisión humana comparten un mismo modelo de verdad

“Se ve bien” puede pasar

REVISIÓN y evidencia no disponible bloquean por defecto

Modelo de seguridad y privacidad

  • Local-first y offline; no se requiere cuenta ni servicio en la nube.

  • contract-safe está habilitado por defecto, sin autorización implícita.

  • Las ediciones esperadas exactas están vinculadas a su cláusula y ocurrencia.

  • Partes, dinero, monedas, fechas, duraciones, porcentajes, encabezados, pies de página, firmas, sellos y anexos están protegidos por defecto.

  • Ed25519 offline es el mecanismo de confianza central; la identidad empresarial se puede agregar mediante adaptadores más adelante.

  • La evidencia por defecto es minimal; full es explícito y sealed está cifrado para destinatarios nombrados.

  • Las respuestas de MCP están acotadas y no devuelven contratos completos, imágenes de páginas ni material de claves.

Lee el diseño de la puerta de verificación aprobado y el registro de aceptación del Plan 4.5 para conocer el modelo completo de confianza y veredicto.

Soporte de formatos actual

  • PDF → PDF

  • DOCX → DOCX

  • DOCX → PDF

  • Estructura de contratos en inglés, chino y bilingüe

  • Renderizado opcional de DOCX mediante LibreOffice; el renderizado de PDF es local

Desarrollo

python -m pip install -e ".[dev]"
python -m pytest -q

El repositorio actualmente contiene cobertura de aceptación de pruebas unitarias, de integración, de manipulación, CLI, MCP, escritorio de revisión, formato cruzado y Golden Path firmado.

Próximos hitos

  • Corpus sintético público reproducible y puerta de cero falsos positivos

  • Acción de GitHub para revisión de solicitudes de extracción de documentos binarios

  • Medios de demostración y Review Bundle de ejemplo descargable

  • Esquemas, modelo de amenazas, SBOM, procedencia y lanzamiento alpha firmado

  • Límites de complementos para analizadores, renderizadores, firmas, cifrado y almacenamiento empresariales

ArtifactDiff se está construyendo en torno a una promesa concreta: cuando un agente edita un contrato, puedes demostrar exactamente qué se le permitió cambiar — y que no cambió nada más.

Available Tools

10 tools
compare_documentsD
ParametersJSON Schema
NameRequiredDescriptionDefault
forceNo
visualNo
after_pathYes
output_dirNo
before_pathYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

D1/5.0
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Tool has no description.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness1/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Tool has no description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Tool has no description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Tool has no description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose1/5

Does the description clearly state what the tool does and how it differs from similar tools?

Tool has no description.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Tool has no description.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

draft_contract_policyD
ParametersJSON Schema
NameRequiredDescriptionDefault
afterYes
anchorYes
beforeYes
headingYes
rule_idYes
clause_labelYes
ancestor_pathNo
baseline_pathYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

D1/5.0
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Tool has no description.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness1/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Tool has no description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Tool has no description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Tool has no description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose1/5

Does the description clearly state what the tool does and how it differs from similar tools?

Tool has no description.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Tool has no description.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_review_findingD
ParametersJSON Schema
NameRequiredDescriptionDefault
finding_idYes
bundle_pathYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

D1/5.0
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Tool has no description.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness1/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Tool has no description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Tool has no description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Tool has no description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose1/5

Does the description clearly state what the tool does and how it differs from similar tools?

Tool has no description.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Tool has no description.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

inspect_contractD
ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

D1/5.0
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Tool has no description.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness1/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Tool has no description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Tool has no description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Tool has no description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose1/5

Does the description clearly state what the tool does and how it differs from similar tools?

Tool has no description.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Tool has no description.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

inspect_documentD
ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes
forceNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

D1/5.0
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Tool has no description.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness1/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Tool has no description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Tool has no description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Tool has no description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose1/5

Does the description clearly state what the tool does and how it differs from similar tools?

Tool has no description.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Tool has no description.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_review_findingsD
ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
cursorNo
bundle_pathYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

D1/5.0
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Tool has no description.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness1/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Tool has no description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Tool has no description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Tool has no description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose1/5

Does the description clearly state what the tool does and how it differs from similar tools?

Tool has no description.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Tool has no description.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

seal_local_policyD
ParametersJSON Schema
NameRequiredDescriptionDefault
output_pathYes
policy_pathYes
baseline_pathYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

D1/5.0
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Tool has no description.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness1/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Tool has no description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Tool has no description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Tool has no description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose1/5

Does the description clearly state what the tool does and how it differs from similar tools?

Tool has no description.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Tool has no description.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

validate_contract_policyD
ParametersJSON Schema
NameRequiredDescriptionDefault
policy_pathYes
baseline_pathYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

D1/5.0
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Tool has no description.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness1/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Tool has no description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Tool has no description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Tool has no description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose1/5

Does the description clearly state what the tool does and how it differs from similar tools?

Tool has no description.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Tool has no description.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

verify_contract_changeD
ParametersJSON Schema
NameRequiredDescriptionDefault
visualNo
output_pathYes
baseline_pathYes
candidate_pathYes
sealed_policy_pathYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

D1/5.0
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Tool has no description.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness1/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Tool has no description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Tool has no description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Tool has no description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose1/5

Does the description clearly state what the tool does and how it differs from similar tools?

Tool has no description.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Tool has no description.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

verify_review_bundleD
ParametersJSON Schema
NameRequiredDescriptionDefault
bundle_pathYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

D1/5.0
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Tool has no description.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness1/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Tool has no description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Tool has no description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Tool has no description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose1/5

Does the description clearly state what the tool does and how it differs from similar tools?

Tool has no description.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Tool has no description.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 10 tool updatesv0.1.0
    • First observedcompare_documents
    • First observeddraft_contract_policy
    • First observedget_review_finding
    • First observedinspect_contract
    • First observedinspect_document
    • First observedlist_review_findings
    • First observedseal_local_policy
    • First observedvalidate_contract_policy
    • First observedverify_contract_change
    • First observedverify_review_bundle

TDQS

D1.9/5.0

Scored across 10 tools

Disambiguation2/5

The tool set includes several pairs with ambiguous boundaries: inspect_document vs inspect_contract likely overlap, and compare_documents vs verify_contract_change seem to address similar tasks. Without descriptions, an agent would struggle to distinguish which tool to call for a given operation.

Naming Consistency5/5

All tools use a clear snake_case verb_noun pattern (compare, inspect, draft, validate, seal, verify, list, get). Consistency is high and follows a predictable convention.

Tool Count5/5

10 tools is within the ideal range for a domain covering document comparison and contract policy management. The count is well-scoped with no obvious bloat.

Completeness4/5

Core workflows for contract policy drafting/validation/sealing and review finding retrieval are covered. However, there is no tool for listing or managing documents/contracts, and the generic artifact diffing implied by the server name is thin, leaving minor but noticeable gaps.

Maintenance

ActivityActive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides cryptographic governance receipts for AI agents, enabling pre-execution evaluation and signed verdicts (EXECUTE/BLOCK/REVIEW/SHADOW) with offline-verifiable audit trails.
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables AI agents to propose wallet payments while a local, human-authored policy decides whether each transaction is approved, requires human confirmation, or is refused, and records every decision in a signed, append-only ledger.
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables users' AI agents to join signed, hash-chained rooms with another person's agent to negotiate, coordinate, or hand off work, while a locally enforced mandate filters every outbound message. Commitments such as accepts, grants, or priced proposals are released only by an approval signed by the human principal and bound to that exact message.
    63 npm
    Apache 2.0