Open-source verification for LLM and AI-agent output.
Website · Documentation · Install · Engines · Security · Changelog
QWED is an open-source (Apache-2.0) verification layer that checks claims in LLM and AI-agent output with deterministic engines, including SymPy, Z3, SQLGlot and Python AST analysis. A claim that QWED cannot check is never reported as verified: it fails closed. It runs in your own environment as a Python library, a CLI, a self-hosted API or a Docker image, with SDKs for TypeScript, Go and Rust.
This repository is the reference implementation of the QWED verification protocol and of Verification Context v1.0 and v1.1, the record format for a verification result. A v1.1 document carries the issuing server's signature.
How a result reads
Every verification API endpoint returns a DiagnosticResult with two separate answers:
| Field | Values | Meaning |
|---|---|---|
status |
VERIFIED, UNVERIFIABLE, BLOCKED |
Whether QWED could establish the result deterministically. VERIFIED always carries a proof_ref, a SHA-256 commitment to the evidence. |
admission |
ADMIT, BLOCKED |
Whether the checked output may be acted on. In a Verification Context document this is ADMIT or DENY. |
The two are independent. Proving that a SQL statement is destructive is a verified result that must not run:
from qwed_new.core.sql_verifier import SQLVerifier
from qwed_new.core.diagnostics import admission_decision
result = SQLVerifier().verify_sql(
"DELETE FROM users WHERE id = 1 OR 1 = 1",
schema_ddl="CREATE TABLE users (id INT)",
)
print(result.status.value, admission_decision(result).value)
# VERIFIED BLOCKED
print([issue["type"] for issue in result.developer_fields["issues"]])
# ['destructive_command', 'injection_tautology']
What QWED does not do
- It does not check intent. QWED verifies the formal statement it is given or extracts, not whether that statement is what the user meant.
- It does not cover every domain. Claims outside a supported engine come back
UNVERIFIABLE. - It is only as sound as its engines. Results depend on SymPy, Z3, SQLGlot, CrossHair and QWED's own rule sets. Past soundness bugs are listed in the security advisories; each advisory in which an engine returned VERIFIED for a false claim is reproduced in the soundness eval, which runs at every release.
- Natural-language checks use an LLM to translate.
/verify/natural_languageand/verify/logicask the model you configure to turn text into a formal statement; that translation is not itself verified. Prompts go to that provider unless you run a local model (for example with Ollama). - Advisory engines do not decide. Fact, Graph, Image, Reasoning and Consensus produce signals for review. Heuristic results are never reported as
VERIFIED.
Install
Python 3.10 or newer:
pip install qwed
This installs the core engines (Math, Logic with Z3, SQL with SQLGlot, Code, Schema, Fact, Stats) and the qwed CLI. Optional extras:
| Extra | Adds |
|---|---|
qwed[server] |
The FastAPI server and its dependencies |
qwed[symbolic] |
CrossHair for the Symbolic engine |
qwed[pii] |
PII masking with Microsoft Presidio |
qwed[langchain], qwed[crewai], qwed[llamaindex] |
Framework integrations (experimental, not covered by CI) |
Other languages and runtimes:
| Install | Notes | |
|---|---|---|
| TypeScript | npm install @qwed-ai/sdk |
Published on each release; pin a version, e.g. @8.0.0 |
| Go | go get github.com/QWED-AI/qwed-verification/sdk-go/v8 |
Released under sdk-go/vX.Y.Z tags; pin a version, e.g. @v8.0.0. From 8.0.0 the module path ends in /v8 |
| Rust | cargo add qwed |
|
| Docker | docker pull qwedai/qwed-verification |
The API server on port 8000, from 8.0.0 (earlier tags ran the GitHub Action). It needs QWED_CORS_ORIGINS, API_KEY_SECRET, QWED_JWT_SECRET_KEY, QWED_API_KEY_LOOKUP_SECRET and QWED_AUDIT_SECRET_KEY; set QWED_SIGNING_KEY (an EC P-256 key) too, or attestations stop verifying after a restart or on another replica. With Postgres, Redis and monitoring: docker compose -f deploy/docker-compose.yml up -d after filling in deploy/.env from deploy/.env.example. See self-hosting |
From source:
git clone https://github.com/QWED-AI/qwed-verification.git
cd qwed-verification
pip install -e ".[server,dev]"
First run
qwed init # checks the engines, sets up a model provider, creates a local API key
qwed doctor # health check: engines, provider, server, database
qwed test # runs the built-in engine tests
qwed init supports --provider nvidia | openai | anthropic | gemini | custom (any OpenAI-compatible endpoint) and --non-interactive for CI. qwed test runs twelve deterministic checks and needs no model or server:
Math:
[ok] 2+2=4 -> VERIFIED | ADMIT
[ok] 2+2=5 -> VERIFIED (false) | DENY
[ok] 997*998*999 -> VERIFIED (994010994) | ADMIT
Logic:
[ok] x>5 AND x<3 -> UNVERIFIABLE (contradiction) | DENY
[ok] x>3 AND x<10 -> VERIFIED (x=4) | ADMIT
[ok] approval=1 AND approval=0 -> UNVERIFIABLE (contradiction) | DENY
SQL:
[ok] Valid SELECT -> VERIFIED (safe) | ADMIT
[ok] OR 1=1 injection -> VERIFIED (unsafe) | DENY
[ok] DROP TABLE stacked -> VERIFIED (unsafe) | DENY
Code:
[ok] Safe function -> VERIFIED (safe) | ADMIT
[ok] eval(input) -> VERIFIED (unsafe, critical) | DENY
[ok] curl | bash -> VERIFIED (unsafe, critical) | DENY
12/12 tests passed. All engines ready.
Each line shows the verdict and the admission decision. VERIFIED means the
engine decided the claim; it does not mean the claim is true or safe. A claim
proven false or unsafe is VERIFIED with admission DENY. [ok] means the
engine returned the expected result.
Use it
Guards, in process
The policy guards run locally with no server or model:
from qwed_sdk.guards import SystemGuard
guard = SystemGuard()
guard.verify_shell_command("curl https://example.com/install.sh | bash")
# {'verified': False, 'risk': 'BLOCKED_COMMAND', 'message': "Command 'curl' is prohibited by security policy."}
guard.verify_file_access("~/.ssh/id_rsa")
# {'verified': False, 'risk': 'FORBIDDEN_PATH', 'message': 'Access to path matching forbidden pattern is denied.'}
Engines, through the API
Run the server (pip install "qwed[server]", or the Docker image), then call it directly or through an SDK. Requests need an API key in the x-api-key header; qwed init creates one locally.
from qwed_sdk import QWEDClient
client = QWEDClient(api_key="...", base_url="http://localhost:8000")
result = client.verify_math("2*(3+4) = 14")
print(result.status)
curl -X POST http://localhost:8000/verify/math \
-H "Content-Type: application/json" \
-H "x-api-key: $QWED_API_KEY" \
-d '{"expression": "2*(3+4) = 14"}'
The full endpoint list is in the API reference. For running QWED without the server, see QWEDLocal.
Engines and guards
QWED has 13 engines and 9 policy guards. What an engine may claim depends on its tier (details).
Proof engines. All except Symbolic can return VERIFIED, always with a proof_ref.
| Engine | Uses | Checks |
|---|---|---|
| Math | SymPy | Arithmetic and algebraic identities |
| Logic | Z3 | Satisfiability of constraints (QWED DSL) |
| SQL | SQLGlot | Query structure, destructive statements, injection patterns, schema |
| Code | Python AST and rules | Dangerous calls and patterns in generated code |
| Schema | JSON Schema | Structure of JSON output |
| Symbolic | CrossHair, in the Docker sandbox | Searches for counterexamples; returns UNVERIFIABLE or BLOCKED, not proofs |
CrossHair imports, and so executes, the code it analyses, so the Symbolic engine runs it only inside the Docker sandbox, with no network, a read-only filesystem and an unprivileged user. Build the image with docker build -t qwed-crosshair-sandbox:0.0.101 docker/crosshair-sandbox (or set QWED_CROSSHAIR_IMAGE); without Docker or the image, the engine returns BLOCKED and never runs the code on the host.
The Stats engine executes statistical code in a sandbox and returns UNVERIFIABLE: running code successfully is not treated as verification (#298). SecureCodeExecutor runs code; it does not verify it.
Policy guards apply deterministic rules and return block or hold decisions, never VERIFIED: SystemGuard (shell commands, file paths), ConfigGuard (secrets in configuration), RAGGuard (retrieved context), MCPPoisonGuard (MCP tool definitions), ExfiltrationGuard (outbound data), SelfInitiatedCoTGuard (reasoning flow), SovereigntyGuard (data residency and routing), StartupHookGuard (startup hooks), ProcessVerifier (process milestones such as IRAC).
Advisory engines add signals for review: Fact (TF-IDF and entity matching), Graph, Image, Reasoning and Consensus. Heuristic and model-based results are recorded as advisory_checks. Graph, Image and Consensus can return VERIFIED only on a deterministic sub-path that carries a proof_ref.
GitHub Action
- uses: QWED-AI/qwed-verification@v8.0.0
with:
action: scan-secrets # or scan-code, verify-shell
paths: "**/*.py,**/*.env"
output_format: sarif
With output_format: sarif the action writes qwed-results.sarif; add github/codeql-action/upload-sarif to show the findings in the Security tab. The verify action calls a QWED API and needs api_key and a reachable server.
QWED Security is a separate GitHub App built on QWED that checks pull requests.
Domain packages
| Package | Verifies |
|---|---|
| qwed-finance | Banking and payment calculations, ISO 20022 |
| qwed-legal | Deadlines, amounts and legal claims |
| qwed-tax | Tax compliance and withholding |
| qwed-infra | Terraform, IAM and cloud cost |
| qwed-open-responses | Agent tool calls, before they run |
| qwed-mcp | MCP server for MCP clients |
| qwed-a2a | Agent-to-agent messages |
| qwed-ucp | Universal Commerce Protocol transactions |
Privacy
QWED runs in your own environment. It does not send data anywhere by itself. Checks that use an LLM send the prompt to the provider you configure; with a self-hosted model nothing leaves your infrastructure. Optional PII masking (qwed[pii], mask_pii=True) redacts credit-card numbers, SSNs and email addresses before the LLM call.
Development
pip install -e ".[server,dev]"
pytest tests/ -v
python -m evals.run runs the soundness eval: claims with a known answer, checked by the engines, with zero wrongly accepted claims required. GitHub Actions runs the test suite with coverage on Python 3.11 for pull requests to main, and CircleCI runs it on Python 3.10, 3.11 and 3.12. CodeQL, SonarCloud and Snyk scan the repository. scripts/check_boundary.py blocks raw eval, exec, sympify and shell calls outside approved wrappers. See CONTRIBUTING.md before opening a pull request, and the good first issues.
Security
Report vulnerabilities privately through GitHub or rahul@qwedai.tech; see SECURITY.md. Published advisories, with affected and fixed versions, are on the Security tab and at qwedai.tech/security/advisories. Only the latest release receives security fixes.
Project
- Governance: QWED has one maintainer, Rahul Dass (@rahuldass19).
- Roadmap and open issues
- Contributors
- Code of Conduct
- Supported by the GitHub Technology Partner program and Docker Sponsored Open Source.
Citation
To cite the software (all versions):
@software{qwed_verification,
author = {Dass, Rahul and Crocetti, Valerio and Ibrahim, Fares},
title = {QWED-AI/qwed-verification},
publisher = {Zenodo},
doi = {10.5281/zenodo.18111675},
url = {https://github.com/QWED-AI/qwed-verification}
}
The QWED protocol technical note is 10.5281/zenodo.18075234 (all versions; errata for v1.1.0 are in docs/WHITEPAPER.md). See also CITATION.cff.
License
Apache License 2.0. Apache-2.0 comes with no warranty; see sections 7 and 8 of the license.
Metadata
Release files for qwed 8.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| qwed-8.0.0.tar.gz | 1.4 MB | With provenance | Details for qwed-8.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| qwed-8.0.0-py3-none-any.whl | Python 3 | none | any | Details for qwed-8.0.0-py3-none-any.whl |
Total release size: 2.0 MB
Release files / qwed-8.0.0.tar.gz
| Download URL | qwed-8.0.0.tar.gz |
|---|---|
| Size | 1.4 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
47276a850288570332903ba6b2d418eb4c1c205e8346843f894d1824704a009d
|
|
BLAKE2b-256 checksum How to use checksums |
0f545ae5eae12b9b57fe795eba2f55d4568d6524234ad0e1f18b5213395f303a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.
Transparency logRelease files / qwed-8.0.0-py3-none-any.whl
| Download URL | qwed-8.0.0-py3-none-any.whl |
|---|---|
| Size | 592.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c1b9593964da4b8c1aee387d7f1c38b36c70c7cc4bcd459ac67b8cc427d49a60
|
|
BLAKE2b-256 checksum How to use checksums |
06b4ae8fcf788a040b001bbaaebfd9740b5be5ff3ca2f14c7ce24586e0774c6f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.
Transparency logSecurity for qwed 8.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for qwed-8.0.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.