Make an AES-256 key from quantum entropy
Time target: about 5 minutes No SDK needed: one HTTPS GET, then standard crypto.
1. Get a token
Create an account at portal.qrypt.com/register and generate a token for the Entropy service. It is shown only once and is billed like a password: keep it secret.
2. Save it in .env
QRYPT_API_KEY=paste-your-token-herePut .env next to the script and add it to .gitignore. Never print it, not even partly (no cat .env). To check it is set, run grep -q '^QRYPT_API_KEY=.' .env && echo set.
3. Run the snippet
Python 3.9+. Uses requests: Cloudflare blocks Python's built-in urllib User-Agent.
python3 -m venv .venv && .venv/bin/pip install requests cryptography python-dotenv# Qrypt quickstart (Python): 1 KiB of quantum entropy -> AES-256-GCM key.
# Setup: pip install requests cryptography python-dotenv
# Then put QRYPT_API_KEY=<your token> in a .env file next to this script.
import base64
import os
import sys
import requests
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from dotenv import load_dotenv
API = os.getenv("QRYPT_BASE_URL", "https://api-eus.qrypt.com") # mock override
# One friendly message per failure. We never retry automatically.
HELP = {None: "QRYPT_API_KEY is missing: add it to .env (see .env.example).",
400: "400: size must be 1-512. It counts KiB, not bytes.",
401: "401: Authorization header missing, malformed or expired.",
403: "403: Token invalid OR monthly quota used. Check the token first.",
429: "429: rate limited. Live can return 429 about 1 s after a single "
"prior call; wait 10 s or more and never loop."}
# 500, 503 and anything else: Qrypt is busy or erroring, so try again later.
# Step 1: read the token from .env. Never print or log it.
load_dotenv()
token = os.getenv("QRYPT_API_KEY") or sys.exit(HELP[None])
# Step 2: fetch 1 KiB of entropy. A custom User-Agent avoids Cloudflare's
# block on Python's urllib; X-Qrypt-Client tags quickstart traffic.
headers = {
"Authorization": f"Bearer {token}", "Accept": "application/json",
"User-Agent": "qrypt-quickstart/1.0", "X-Qrypt-Client": "quickstart-py/1.0"}
try:
resp = requests.get(f"{API}/api/v1/quantum-entropy", params={"size": 1},
headers=headers, timeout=15)
if resp.status_code != 200:
sys.exit(HELP.get(resp.status_code, f"{resp.status_code}: Qrypt is "
"busy or erroring. Try again shortly."))
entropy_b64 = resp.json()["random"][0]
except (requests.RequestException, ValueError):
sys.exit("Network error or non-JSON reply: check proxy/TLS, retry soon.")
# Step 3: decode first, then keep 32 bytes as the AES-256 key.
key = base64.b64decode(entropy_b64)[:32]
# Step 4: encrypt and decrypt a sample message with AES-256-GCM.
aes, nonce, message = AESGCM(key), os.urandom(12), b"hello, quantum entropy"
assert aes.decrypt(nonce, aes.encrypt(nonce, message, None), None) == message
print("Success: AES-256-GCM round trip with a Qrypt key (not shown).")
.venv/bin/python quickstart.pyNode 20.12+ (built-in fetch and process.loadEnvFile). No dependencies.
// Qrypt quickstart (Node 20.12+): 1 KiB of quantum entropy -> AES-256-GCM key.
// No dependencies. Put QRYPT_API_KEY=<your token> in a .env file here.
import assert from "node:assert";
import { existsSync } from "node:fs";
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
const API = process.env.QRYPT_BASE_URL ?? "https://api-eus.qrypt.com"; // mock
// One friendly message per failure. We never retry automatically.
const HELP = { missing: "QRYPT_API_KEY is missing: add it to .env.",
network: "Network error or non-JSON reply: check proxy/TLS, retry soon.",
400: "400: size must be 1-512. It counts KiB, not bytes.",
401: "401: Authorization header missing, malformed or expired.",
403: "403: Token invalid OR monthly quota used. Check the token first.",
429: "429: rate limited. Live can return 429 about 1 s after a single " +
"prior call; wait 10 s or more and never loop." };
// 500, 503 and anything else: Qrypt is busy or erroring, so try again later.
const fail = (msg) => { console.error(msg); process.exit(1); };
// Step 1: read the token from .env. Never print or log it.
if (existsSync(".env")) process.loadEnvFile(".env");
const token = process.env.QRYPT_API_KEY || fail(HELP.missing);
// Step 2: fetch 1 KiB of entropy (size counts KiB, not bytes).
const headers = { Authorization: `Bearer ${token}`, Accept: "application/json",
"User-Agent": "qrypt-quickstart/1.0", "X-Qrypt-Client": "quickstart-js/1.0" };
const url = `${API}/api/v1/quantum-entropy?size=1`;
const res = await fetch(url, { headers }).catch(() => fail(HELP.network));
if (res.status !== 200) fail(HELP[res.status] ??
`${res.status}: Qrypt is busy or erroring. Try again shortly.`);
const body = await res.json().catch(() => fail(HELP.network));
// Step 3: decode first, then keep 32 bytes as the AES-256 key.
const key = Buffer.from(body.random[0], "base64").subarray(0, 32);
// Step 4: encrypt and decrypt a sample message with AES-256-GCM.
const GCM = "aes-256-gcm", iv = randomBytes(12), message = "hello, quantum";
const enc = createCipheriv(GCM, key, iv);
const sealed = Buffer.concat([enc.update(message, "utf8"), enc.final()]);
const dec = createDecipheriv(GCM, key, iv).setAuthTag(enc.getAuthTag());
assert.equal(dec.update(sealed, null, "utf8") + dec.final("utf8"), message);
console.log("Success: AES-256-GCM round trip with a Qrypt key (not shown).");
node quickstart.mjsWhat the API returns
One base64 string per KiB requested. Decode it before use; it never contains the key itself.
{
"random": ["<base64 string: 1,024 random bytes when decoded>"],
"size": 1
}
4. Expected output
Success: AES-256-GCM round trip with a Qrypt entropy key (key not shown).
Things that trip people up
sizecounts KiB, not bytes. Ask forsize=1(1,024 bytes) and take 32 bytes.- Base64-decode the string first; never use the text itself as the key.
- Live can return 429 about 1 s after a single prior call; wait 10 s or more and never loop.
- A wrong token returns 403, the same as an exhausted quota. Check the token first.
- Never print, log or commit the key or token. Use fresh entropy for each key.
Errors? See Troubleshooting.
Using an AI coding agent?
In our tests, AI agents read part of .env (for example head -c 20 .env) in 14 of 15 runs, even when these docs told them not to. A doc can't stop that habit, but file permissions can. Run your agent inside this small Docker sandbox:
- Download the sandbox files: Dockerfile, entrypoint.sh, qrypt-run, qrypt-run-inner, run.sh, all into one folder called
agent-sandbox/. Then runchmod +x agent-sandbox/*.sh agent-sandbox/qrypt-run*. - Start the agent inside it (the project folder must already contain your
.env):agent-sandbox/run.sh ~/my-project ~/my-project/.env your-agent-command - Inside the sandbox, the agent runs as user
agent..envbelongs to a separate user (qrypt, mode 600), socat .envgets Permission denied. The program runs withqrypt-run quickstart.py, a fixed rule that runs one.py/.mjs/.jsfile as theqryptuser, so it can still readQRYPT_API_KEYfrom.env.
The agent CLI has to be available in the container. Either add it to the Dockerfile (for example RUN npm i -g your-agent) or mount it by setting SANDBOX_DOCKER_ARGS. For example, for Cursor CLI: SANDBOX_DOCKER_ARGS="-v $HOME/.local/share/cursor-agent/versions/VERSION:/opt/cursor-agent:ro -v $HOME/agent-home:/home/agent -e PATH=/opt/cursor-agent:/usr/local/bin:/usr/bin:/bin", with your agent's login file copied into ~/agent-home.
Limits: this stops accidental reads such as cat, head or an editor opening .env. It does not stop an agent that deliberately writes a program to print the token and runs it with qrypt-run. Review what your agent runs, and use a test token, not a production one.