Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
891f1b6
fix: validate QEC resource estimator inputs
Pyhroff Oct 9, 2026
a0a2536
security: restrict QEC API CORS origins
Pyhroff Oct 9, 2026
cb48298
docs: document local API origin policy and estimate limits
Pyhroff Oct 9, 2026
b8ffa98
test: cover QEC lattice and resource invariants
Pyhroff Oct 9, 2026
8506ce4
ci: add deterministic QEC regression tests
Pyhroff Oct 9, 2026
c555a75
fix: validate estimator numeric inputs safely
Pyhroff Oct 9, 2026
a7d5d9b
fix: correct server imports and restrict CORS origins
Pyhroff Oct 9, 2026
176e865
fix: reject boolean resource estimator counts
Pyhroff Oct 9, 2026
deea5b0
test: expand QEC reproducibility and model invariants
Pyhroff Oct 9, 2026
f5afe52
docs: clarify QEC model validation and reproducibility limits
Pyhroff Oct 9, 2026
006714d
feat: add reproducible QEC resource parameter sweep
Pyhroff Oct 9, 2026
d0fc69c
test: verify QEC sweep reproducibility manifest
Pyhroff Oct 9, 2026
3cd699a
ci: smoke-test reproducible QEC resource sweep
Pyhroff Oct 9, 2026
dc0e618
fix: bound logical-error probabilities outside model domain
Pyhroff Oct 9, 2026
fded944
feat: expose resource model validity and cap status
Pyhroff Oct 9, 2026
98b022c
fix: do not mark capped or above-threshold estimates feasible
Pyhroff Oct 9, 2026
efea151
test: flag invalid QEC resource-model extrapolations
Pyhroff Oct 9, 2026
5ddafe4
fix: keep resource slider within model threshold domain
Pyhroff Oct 9, 2026
145abdb
fix: restrict resource API to below-threshold model inputs
Pyhroff Oct 9, 2026
c7d0782
fix: distinguish invalid-domain and capped resource estimates
Pyhroff Oct 9, 2026
f48960f
docs: document resource sweep and model-domain limits
Pyhroff Oct 9, 2026
b6402d8
fix: omit benchmark ratio for capped or out-of-domain estimates
Pyhroff Oct 9, 2026
e07d9ec
test: suppress benchmark ratios for invalid resource estimates
Pyhroff Oct 9, 2026
7f6ec38
ci: run the full resource parameter grid
Pyhroff Oct 9, 2026
b4dcb9b
docs: publish measured analytical resource-sweep table
Pyhroff Oct 9, 2026
19e9411
docs: align API and UI resource-rate ranges
Pyhroff Oct 9, 2026
662353b
docs: clarify resource sweep feasibility threshold
Pyhroff Oct 9, 2026
bd7aaef
feat: add seeded Monte Carlo resource scenarios
Pyhroff Oct 9, 2026
50c42c5
feat: expose resource scenario uncertainty endpoint
Pyhroff Oct 9, 2026
f07949b
test: cover seeded resource scenario uncertainty
Pyhroff Oct 9, 2026
ea7b281
docs: explain resource scenario uncertainty
Pyhroff Oct 9, 2026
064f96a
feat: bridge observed PQC findings into scoped QEC estimates
Pyhroff Oct 9, 2026
b8169e7
test: verify scenario bridge evidence and model boundaries
Pyhroff Oct 9, 2026
171f6df
test: add Quantum Collapse scenario bridge fixture
Pyhroff Oct 9, 2026
ad44f67
ci: smoke-test PQC-to-QEC scenario bridge
Pyhroff Oct 9, 2026
334753e
docs: explain PQC evidence to QEC resource bridge
Pyhroff Oct 9, 2026
4cbdcfc
docs: clarify bridge output metadata
Pyhroff Oct 9, 2026
61fb99a
Merge current base into QEC bridge feature branch
Pyhroff Oct 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches: [master]
pull_request:
branches: [master, upgrade/qec-validation-security-20261009]
branches: [master]

permissions:
contents: read
Expand Down Expand Up @@ -41,6 +41,23 @@ jobs:
assert report["code_revision"]
print(f"Resource sweep passed: {report['result_count']} cases, {report['runtime_seconds']} seconds")
PY
- name: Run PQC evidence-to-QEC bridge smoke test
run: |
python -m qec.scenario_bridge tests/fixtures/quantum_collapse_scenario.json --n-logical 100 --t-gates 1000000 --output /tmp/qec-bridge.json
python - <<'PY'
import json
with open("/tmp/qec-bridge.json", encoding="utf-8") as handle:
report = json.load(handle)
assert report["measurement_type"] == "conditional_analytical_resource_estimate_not_hardware_measurement"
assert report["summary"]["supported_rsa_node_count"] == 1
assert report["summary"]["unsupported_node_count"] == 1
rows = {row["crypto"]: row for row in report["findings"]}
assert rows["RSA"]["status"] == "estimated"
assert rows["RSA"]["resource_estimate"]["n_logical"] == 100
assert rows["ECC-P256"]["status"] == "unsupported_crypto_model"
assert rows["ECC-P256"]["resource_estimate"] is None
print("PQC-to-QEC bridge passed: RSA estimate scoped; ECC retained as unsupported evidence")
PY
- name: Compile Python sources
run: python -m compileall -q .

26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,32 @@ docs/

---

## PQC evidence → QEC resource scenario bridge

QEC Lab can consume the versioned scenario JSON produced by Quantum Collapse's
PQC Scanner bridge. It retains the input finding references and runs the existing
resource estimator **only for RSA findings**, because the current estimator models
RSA-2048 factoring. ECC, DH, and DSA observations are preserved with an explicit
`unsupported_crypto_model` status rather than being treated as RSA equivalents.

From `backend/`:

```bash
python -m qec.scenario_bridge /path/to/scenario.json \
--p-physical 0.001 --n-logical 3000 --t-gates 3000000000 \
--p-total-failure 0.5 --output qec-resource-scenario.json
```

Each output labels itself `conditional_analytical_resource_estimate_not_hardware_measurement`.
It carries the estimator's model-domain and distance-cap flags and documents that
the result is assumption-sensitive, omits architecture-specific overhead, and does
not prove source-code reachability, deployed use, or an attack timeline. Input
scenario risk scores and dependency assumptions are not recomputed or treated as
physical evidence.

CI exercises this bridge using a small fixture containing one RSA and one ECC
node; the fixture is a contract/regression test, not a benchmark result.

## Scenario uncertainty analysis

The API endpoint `GET /api/resource/uncertainty` runs a seeded Monte Carlo sweep over explicitly supplied ranges for physical error rate, logical-qubit count, T-gate count, and total failure budget. It reports p05/p50/p95, mean, sample standard deviation, and rates for cap-exceeded, model-domain-valid, and feasible scenarios.
Expand Down
140 changes: 140 additions & 0 deletions backend/qec/scenario_bridge.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
"""Consume Quantum Collapse bridge output and add scoped QEC resource estimates.

Only RSA findings are supported by the current RSA-2048 resource model. ECC/DH/DSA
findings are retained as observed evidence but never coerced into RSA estimates.
All generated values are analytical scenario outputs, not hardware measurements.
"""
from __future__ import annotations

import argparse
import json
from pathlib import Path
from typing import Any

from .resource import estimate


def build_scenario_report(
bridge: dict[str, Any],
*,
p_physical: float = 0.001,
n_logical: int = 3000,
t_gates: int = 3_000_000_000,
p_total_failure: float = 0.5,
) -> dict[str, Any]:
"""Add an RSA-2048 estimate for supported nodes in a bridge scenario."""
if not isinstance(bridge, dict) or bridge.get("schema_version") != "1.0":
raise ValueError("input must be a Quantum Collapse bridge output with schema_version 1.0")
source = bridge.get("source")
scenario = bridge.get("modeled_scenario")
evidence = bridge.get("observed_evidence")
if not isinstance(source, dict) or not isinstance(scenario, dict) or not isinstance(evidence, dict):
raise ValueError("input must contain source, observed_evidence, and modeled_scenario objects")
nodes = scenario.get("nodes")
if not isinstance(nodes, list):
raise ValueError("modeled_scenario.nodes must be a list")

rows = []
for node in nodes:
if not isinstance(node, dict):
raise ValueError("each modeled scenario node must be an object")
crypto = node.get("crypto")
if not isinstance(crypto, str):
raise ValueError("each scenario node must include a crypto label")
evidence_ref = {
"node_id": node.get("id"),
"file": node.get("evidence_file", ""),
"line": node.get("evidence_line"),
"algorithm": node.get("evidence_algorithm", crypto),
}
if crypto == "RSA":
model_result = estimate(
p_physical=p_physical,
n_logical=n_logical,
t_gates=t_gates,
p_total_failure=p_total_failure,
)
rows.append({
**evidence_ref,
"crypto": crypto,
"status": "estimated",
"resource_estimate": model_result,
"limitation": "Estimate is scoped to RSA-2048 factoring; it does not establish runtime reachability or an attack timeline.",
})
else:
rows.append({
**evidence_ref,
"crypto": crypto,
"status": "unsupported_crypto_model",
"resource_estimate": None,
"limitation": "Current QEC resource model is scoped to RSA-2048 factoring; no RSA estimate is assigned to this finding.",
})

supported = sum(row["status"] == "estimated" for row in rows)
return {
"schema_version": "1.0",
"experiment": "qec-lab-quantum-collapse-scenario-bridge",
"measurement_type": "conditional_analytical_resource_estimate_not_hardware_measurement",
"input": {
"format": source.get("format"),
"format_version": source.get("version"),
"source_tool": source.get("tool"),
"bridge_schema_version": bridge.get("schema_version"),
"observed_finding_count": evidence.get("finding_count"),
},
"resource_model": {
"name": "simplified_surface_code_rsa2048_estimator",
"scope": "RSA-2048 factoring only",
"parameters": {
"p_physical": p_physical,
"n_logical": n_logical,
"t_gates": t_gates,
"p_total_failure": p_total_failure,
},
"limitations": [
"analytical approximation, not a hardware measurement or calibrated forecast",
"omits architecture-specific routing, magic-state factory, and layout overhead",
"reported distance 61 is a search-cap sentinel, not a validated minimum-distance solution",
"source-code findings do not prove runtime reachability or deployed use",
"ECC, DH, and DSA findings are preserved but unsupported by this RSA-2048 model",
],
},
"summary": {
"scenario_node_count": len(nodes),
"supported_rsa_node_count": supported,
"unsupported_node_count": len(rows) - supported,
"edge_count": scenario.get("edge_count", 0),
},
"findings": rows,
}


def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("scenario", type=Path, help="Quantum Collapse SARIF bridge output JSON")
parser.add_argument("--p-physical", type=float, default=0.001)
parser.add_argument("--n-logical", type=int, default=3000)
parser.add_argument("--t-gates", type=int, default=3_000_000_000)
parser.add_argument("--p-total-failure", type=float, default=0.5)
parser.add_argument("--output", type=Path)
args = parser.parse_args()
try:
bridge = json.loads(args.scenario.read_text(encoding="utf-8"))
report = build_scenario_report(
bridge,
p_physical=args.p_physical,
n_logical=args.n_logical,
t_gates=args.t_gates,
p_total_failure=args.p_total_failure,
)
except (OSError, json.JSONDecodeError, ValueError) as exc:
parser.error(str(exc))
rendered = json.dumps(report, indent=2, sort_keys=True) + "\n"
if args.output:
args.output.write_text(rendered, encoding="utf-8")
print(rendered, end="")
return 0


if __name__ == "__main__":
raise SystemExit(main())
60 changes: 60 additions & 0 deletions backend/tests/fixtures/quantum_collapse_scenario.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"schema_version": "1.0",
"source": {
"format": "SARIF",
"version": "2.1.0",
"tool": "pqc-scanner"
},
"observed_evidence": {
"finding_count": 2,
"by_bucket": {
"quantum_broken": 2,
"classically_broken": 0,
"quantum_weakened": 0,
"other": 0
},
"findings": []
},
"modeled_scenario": {
"node_count": 2,
"edge_count": 0,
"finding_weighted_risk_score": 0.5,
"nodes": [
{
"id": "finding-0001",
"sector": "Scanned source finding",
"crypto": "RSA",
"criticality": 5,
"migration_pct": 0,
"evidence_file": "src/auth.py",
"evidence_line": 12,
"evidence_algorithm": "RSA",
"modeled_risk": 0.8
},
{
"id": "finding-0002",
"sector": "Scanned source finding",
"crypto": "ECC-P256",
"criticality": 5,
"migration_pct": 0,
"evidence_file": "src/exchange.py",
"evidence_line": 30,
"evidence_algorithm": "ECC/ECDSA/ECDH",
"modeled_risk": 0.8
}
],
"unmapped_quantum_findings": [],
"assumptions": {
"migration_pct": 0,
"criticality_by_sarif_level": {
"error": 5,
"warning": 3,
"note": 1
},
"dependency_edges": "none inferred; scanner output does not establish dependency topology",
"risk_score": "mean of modeled risk for mapped quantum-broken findings only",
"risk_coefficients": "illustrative values from quantum_collapse.risk",
"limitations": "static findings do not prove runtime reachability or deployed usage"
}
}
}
Loading
Loading