Qedra docs
How the lab works under the hood — for builders, reviewers and the curious.
Overview
Qedra is an agent-operated workspace for small quantum experiments. It has three separately deployed parts: a Next.js frontend, a FastAPI service with a Python worker (Qiskit, Qiskit Aer, SQLite or PostgreSQL), and optional execution receipts on Robinhood Chain. The agent plans and explains; it never executes anything itself.
Setup
See the repository README for full instructions. In short: run the API (uvicorn app.main:app in api/), then the frontend with API_URL pointing at it (npm run dev in web/). Contract deployment lives in contracts/ (Hardhat). Without API_URL the frontend still builds and runs; compute features show as offline.
Backends
- local_simulator — Qiskit Aer statevector. Exact probabilities plus seeded finite-shot counts. Free, runs in an isolated child process with qubit, gate, shot, time and memory budgets.
- ibm_hardware — SamplerV2 on IBM Quantum, only when the server has credentials and an allowed backend list, and only with per-job human approval. Returns noisy sampled counts only: no exact probabilities and no seed. Queue time and cost are not estimated; IBM bills the credential owner. Max-Cut is simulator-only.
Simulator and hardware results are never mixed or labelled interchangeably.
Circuit schema
Jobs use a validated JSON circuit (version 1). The service never parses or runs uploaded code. Gates: h x y z s sdg t tdg (1 qubit), rx ry rz (1 qubit + 1 angle in radians), cx cz swap (2 qubits), mcz (multi-controlled Z on 2+ qubits). All qubits are measured at the end. Bitstrings use Qiskit order: the rightmost character is qubit 0. OpenQASM 2.0 export is available per job.
{
"spec": {
"template": "custom",
"circuit": {
"version": 1,
"num_qubits": 2,
"gates": [
{ "op": "h", "qubits": [0], "params": [] },
{ "op": "cx", "qubits": [0, 1], "params": [] },
{ "op": "rz", "qubits": [1], "params": [0.5] }
]
}
},
"backend": "local_simulator",
"shots": 1024,
"seed": 7
}Experiments
- bell, ghz (
num_qubits) — entangled states; analysis reports the fraction of shots on the ideal outcomes. - grover (
marked, 2–4 bits) — labelled educational; reports the ideal success probability and the observed rate. - maxcut (
edges,p∈ {1, 2}) — QAOA with angles optimised on the simulator, always compared against an exact classical solver. The result says plainly which method won (at these sizes, at best a tie). - teleport (
theta,phi) — sends ry(θ)·rz(φ)|0⟩ from qubit 0 to qubit 2. Written in deferred-measurement form: the classically controlled X/Z corrections become CX/CZ gates, which gives the same statistics because every qubit is measured once at the end. Bob then applies the inverse preparation, so a perfect teleport reads 0 on qubit 2 — this checks the phase as well as the angle. - qrng (
num_qubits) — equal superposition on every qubit; per-shot bitstrings become random bits (hex), with a monobit frequency test and a χ² uniformity test. On the simulator the bits are seeded pseudo-random (reproducible), not quantum randomness; only hardware runs give physical randomness. - superdense (
message, 2 bits) — two classical bits sent with one qubit of a shared Bell pair; reports the decoding success rate. - deutsch_jozsa (
num_qubitsinputs,oracle∈ constant_0 | constant_1 | balanced) — one helper qubit is added (the leftmost bit). All-zero inputs mean constant; anything else means balanced. Classically, certainty can take 2n−1+1 queries. - bernstein_vazirani (
secret, 1–7 bits) — recovers the hidden string f(x) = s·x mod 2 in one query instead of n. - custom (
circuit) — your own circuit in the schema above.
Agent
The planner calls Claude with server-side credentials and exactly one tool, propose_plan, with a strict schema. Proposals go through the same validation as manual jobs; invalid ones are sent back to the model to fix. A human must press Run. The agent cannot run code, sign transactions, move funds, change limits or submit hardware jobs. Without a configured key, planning reports itself as unavailable.
MCP
/api/mcp speaks Streamable HTTP with no key. Tools: plan_experiment, run_experiment (simulator only), get_job, verify_receipt, get_limits, token_safety_report and quantum_exposure. See Agents · MCP for client setup.
On-chain agents
Two free, read-only agents read Robinhood Chain mainnet (4663), whichever network receipts use. They never sign or send transactions, and work from event logs and current state only (public nodes don't keep old state). Set QEDRA_AGENTS_RPC_URL to a dedicated RPC for heavier use; the public one rate-limits.
Token Safety Agent
POST /api/agents/token-safety starts a report; poll GET /api/agents/reports/{id}. Checks, all computed by code with evidence attached: proxy/upgradeability (EIP-1967 and minimal proxies); owner powers found as function selectors in the bytecode (mint, blacklist, pause, fees, trading switch, limits); owner() and renouncement; complete mint and burn totals (dedicated log queries, so burns to 0x0 are told apart from transfers to 0x…dEaD and from new mints); holder balances from the full transfer history (capped, then current balances); pool detection via token0()/token1(); the deployer (sender of the first mint) and its quantum exposure. The risk level is the most serious finding. Claude only rewrites the verified findings in plain language; without credentials a template summary is used. Not simulated: sell tax or honeypot behaviour, liquidity locks, the team.
A finished report is stored as canonical JSON. GET /api/agents/reports/{id}/file returns those exact bytes, whose SHA-256 is the report_commitment, so the file works on Verify and can be receipted with record(request_commitment, report_commitment).
Quantum Guardian
POST /api/agents/exposure checks one address. A wallet's public key is exposed once it has sent a transaction (nonce > 0) or signed an EIP-7702 delegation; contracts are judged by their owner or Safe signers. Risk is high when exposed and holding at least 0.001 ETH. The watch list (/api/agents/guardian, tied to the browser session) is re-checked every QEDRA_GUARDIAN_INTERVAL_SECONDS (default 600) and raises alerts when a key becomes exposed or funds land on an exposed wallet. The migration helper only prepares transactions; the user signs each one in their own wallet. Seed phrases are never requested.
Jobs
Status flows queued → running → completed | failed | cancelled, with cancel_requested as an in-flight state. Provider and receipt states are tracked separately. The worker claims one job at a time atomically and runs each in a fresh child process. Updates stream over server-sent events with polling as fallback. An Idempotency-Key header returns the original job for an identical body and a 409 for a different one. Jobs belong to an anonymous browser session; other sessions get a 404. No wallet is needed to run jobs.
Receipts & manifests
Canonical JSON (qedra-canonical-json-v1: sorted keys, no whitespace, UTF-8) produces two SHA-256 commitments:
- Job commitment — over the spec, backend, shots and seed.
- Result commitment — over the exact bytes of the manifest file.
The manifest contains no prompts, credentials or personal data. A receipt is one transaction on Robinhood Chain, signed by the user's own wallet, calling ReceiptRegistry.record(jobCommitment, resultCommitment), which emits Receipt(bytes32 indexed, bytes32 indexed, address indexed submitter). The contract has no owner, no funds and no upgrade path; the fee is the network's ETH gas. A receipt proves a file existed unchanged at a block — it does not independently prove correct quantum execution.
Limits
- Simulator
- 8 qubits · 256 gates · 8,192 shots · 30s wall clock
- Memory
- 3,072 MB per job
- Max-Cut graphs
- 8 nodes · 16 edges
- Jobs
- 60 per hour per session
- IBM hardware
- not configured on this server
- Agent
- 60 planning requests per hour
Loading server limits…
All limits are server configuration (QEDRA_* environment variables). We report results as measured and make no quantum-advantage claims, invented benchmarks, prices or capacity figures; seeded reproducibility is claimed for the simulator only.
Architecture
browser ──► Next.js (web/) ──/api/*──► FastAPI (api/) ──► SQLite / PostgreSQL │ │ ▲ │ ▼ │ claim (compare-and-set) │ worker ──► child process: Qiskit Aer │ └──► IBM Quantum (optional, approved) └── wallet ──► ReceiptRegistry on Robinhood Chain ◄── read-only RPC (API, /verify)
Long-running work never runs in request handlers: the API validates and enqueues, and the worker executes. The server never holds a signing key.
Deployment
- Frontend — any Next.js host (e.g. Vercel). Set
API_URLto proxy/api/*to the compute API; leave it unset for a frontend-only preview. Chain settings useNEXT_PUBLIC_CHAIN_ID,NEXT_PUBLIC_RECEIPT_REGISTRY,NEXT_PUBLIC_RECEIPT_REGISTRY_FROM_BLOCKandNEXT_PUBLIC_TOKEN_ADDRESS. - API + worker — a long-running Python host (container or VM). Run the worker separately in production (
QEDRA_EMBEDDED_WORKER=false,python -m app.worker). - Contracts —
npx hardhat run scripts/deploy.js --network robinhoodTestnet. Mainnet (chain 4663) requires an explicit confirmation flag.