Attested relay v2
A Rust Nitro Enclave relays HTTPS requests and hosts password-tagged pastes. Both use the same daily encryption keys and public RandomX recovery puzzles. Clients verify Nitro attestation before sending data through inner TLS carried in ordinary HTTPS GETs. Cloudflare and the parent transport ciphertext.
Production 190b518 launched on 2026-09-14 and is warming on 24 Graviton5
cores. The c9g.8xlarge parent has 32 physical cores and 64 GiB RAM. The enclave
reserves CPUs 1–24 and 8 GiB; parent services have CPUs 0 and 25–31. Generation
runs at nice 19, but reserved enclave CPUs cannot be lent to parent workloads.
Previous puzzles continue solving on the parent and Hetzner. The release uses isolated process workers and smaller ARM JIT permission updates,
while retaining socket cleanup and publication recovery.
Current evidence.
The production puzzle requires 465,000,000 sequential RandomX hashes: 96 groups of 4,843,750. Generation schedules these groups across 24 workers. At 770 sequential hashes/second, recovery takes about 6.99 days from puzzle publication. This is a calibration target, not a hardware-independent lower bound. Published older 7- and 84-group puzzles remain supported and unchanged.
Commands: send_request accepts GET, HEAD, POST, PUT, PATCH, DELETE and
OPTIONS; write_pastebin and read_pastebin_tag support shared password tags.
Request bodies and individual pastes are limited to 100 KiB. Opaque tag IDs are
public, allowing grouping and counts. Use long random tags because public IDs
allow offline guessing. Protocol, Python client,
threat model.
PyPI 0.2.0a4 includes these commands and 96-group support, with native solver wheels for macOS ARM and Linux ARM/x86. Short-work non-debug Nitro tests passed 100 KiB POST through Mullvad, paste write/read, pagination, tag isolation, and independent recovery of request headers, body and response. Public GET transport and anonymous S3 artifact checks passed. Two fresh GitHub ARM builds reproduced the production Docker image and PCR0/1/2; signed report and EIF verification passed. Release and test evidence.
Live key-production dashboard
shows aggregate generation progress, throughput, ETA and readiness, using
/v1/key-production.
These are host-reported operational values, not signed attestation. Production
rejects commands until its first full-work puzzle is published. Full-work
completion, daily rollover and recovery of the new epoch remain pending. An
automatic follower is running and will solve the new puzzle after publication.
Recovered keys currently stay on solver hosts; automatic public key/plaintext publication remains unfinished. The archive uses 30-day COMPLIANCE Object Lock for stored versions. S3 upload is asynchronous: the enclave waits for a host persistence acknowledgement, not verified S3 storage. The encrypted archive is public.
The existing tlproxy-enclave binary and its deployed drand/age service are the
legacy system, documented in README-v1.md. Its published PCRs and
reviews do not establish anything about this new binary.
Protocol
Python SSL MemoryBIO
-> outer HTTPS GET /relay?reqid=...&seq=...&ack=...&payload=...&send=...
-> Cloudflare
-> bounded Rust host buffer
-> vsock
-> attested-relay-enclave: inner TLS terminates here
-> certificate-verified HTTPS request to upstream
The inner certificate is self-signed. Before sending an upstream URL, the client
verifies the AWS Nitro certificate chain and COSE signature, its fresh nonce,
its independently pinned PCR0, nonzero measurements, and the binding between
public_key in the attestation and the actual inner TLS peer's SPKI. Signed
user_data contains the service signing key, protocol/work parameters, hardware
result and readiness. It never trusts those fields merely because an outer JSON
response says them.
The host transport starts at seq=0&ack=-1. Each next operation acknowledges the
previous response; an exact retry returns the cached response without duplicating
bytes. send=false buffers an input chunk; send=true flushes buffered bytes and
polls output. Empty payloads poll subsequent output. more is a hint; TLS/HTTP
framing determines completeness. Sessions have input/output limits, bounded
admission and idle/absolute deadlines. Every outer response forbids caching.
Inside TLS, /f/https/example.com/path?query performs an HTTPS GET. The MVP
supports port 443, at most five redirects, a 30-second upstream deadline and
10MiB of captured response bodies across the redirect chain. IPs are checked
inside the enclave after DNS resolution; private, metadata and special-purpose
destinations are rejected. No billing or capability-token service is required.
A tool that literally only fetches URLs cannot perform inner TLS or verify COSE without additional cryptographic execution. It can fetch public artifacts and carry ciphertext supplied by a capable client. This implementation does not claim that merely reading an attestation JSON proves its authenticity.
Public recovery
RandomX 2.0.1, upstream commit
aaafe71322df6602c21a5c72937ac284724ae561, algorithm v2, is vendored with
source checksums. Ninety-six independent 32-byte seeds are processed by a bounded worker pool
against one shared dataset. Each iteration includes a domain, epoch, segment,
iteration number and previous output. Each segment's final output derives an
AEAD key that wraps the next seed; the last wraps the epoch key. Only seed one
is public. The manifest is signed by the attested ephemeral Ed25519 service key.
A solver must obtain each next seed before starting its segment. Full and light modes calculate the same native RandomX function. Checkpoints retain progress; manifest binding, per-segment authentication and the final key commitment detect incorrect results. No AWS, KMS, drand or operator secret is needed to solve a published puzzle and decrypt its corresponding published ciphertext.
The generator schedules 96 groups across its available CPUs; the solver processes all 96 sequentially. This is an assumption-dependent computational delay, not a proven VDF or a guaranteed minimum number of wall-clock days. Faster hardware or algorithmic improvements can reveal data earlier. Benchmarks must separately measure solo solving and parallel generation; CPU contention can reduce the intended wall-clock ratio.
At startup the enclave warms up by generating its first puzzle. It publishes that puzzle before activating the key. While serving the current epoch it privately generates the next puzzle. It does not publish the next puzzle until the current epoch ends. An expired epoch never admits new requests, including when generation or publication is late. Already admitted requests retain a bounded lease to finish encrypting their records.
The proposed daily schedule measures delay from puzzle publication/activation, not from each individual request. A record made late in a 24-hour epoch has up to one day less remaining work than the first record; faster implementations also shorten the elapsed delay.
Records use XChaCha20-Poly1305 with a per-puzzle HKDF-derived key and authenticated epoch, sequence, version and puzzle identity. Both successful and failed relay operations are recorded. The response is returned only after the encrypted artifact is acknowledged by the host. A dishonest host can lie about storage; independent replicas and client-held artifacts are still needed for availability. No system can recover a ciphertext and puzzle if every copy is destroyed.
The deployed 190b518 revision uses canonical CBOR plaintext records (schema
version 3, including the software version); the previous 474083e image used
JSON version 2. The encrypted envelope remains compatible, and native decryption
recovers either format as bytes. Direct NSM entropy is required before TLS
initialization, and signing and puzzle secrets come directly from NSM. This
bootstrap passed real Nitro validation in both the diagnostic and production image.
Puzzle manifests are signed; individual records use epoch-key AEAD authentication. After that key becomes public, original-record provenance additionally requires a previously trusted ciphertext digest, such as the artifact name returned over the verified TLS session, or an independent publication record.
Build and local verification
cargo test -p tlproxy-host
cargo test -p relay-timelock
cargo test -p tlproxy-enclave --bin attested-relay-enclave
cargo build -p tlproxy-host
cargo build -p tlproxy-enclave --bin attested-relay-enclave
cargo build -p relay-timelock --bin relay-timelock
python3 -m venv /tmp/attested-relay-venv
/tmp/attested-relay-venv/bin/pip install -e python/attested-relay pytest
/tmp/attested-relay-venv/bin/pytest python/attested-relay/tests
For an explicitly insecure local test, run the host and enclave in separate terminals:
target/debug/tlproxy-host --dev --no-forwarder --http-listen 127.0.0.1:9080 \
--records-dir /tmp/relay-v2-records
target/debug/attested-relay-enclave --dev
Then:
from attested_relay import Relay
relay = Relay("http://127.0.0.1:9080", expected_pcr0="",
insecure_local=True, allow_dev=True)
response = relay.get("https://example.com/")
print(response.status_code, response.text)
This development mode uses short genuine RandomX chains and has no Nitro attestation or timing security. The production client requires an exact PCR0 and verified Graviton5 hardware. Install the published early prereleases with:
pip install attested-relay==0.2.0a4 'attested-relay-timelock[follow]==0.2.0a4'
attested-relay is a pure Python wheel. attested-relay-timelock provides native wheels for macOS ARM64 (15+) and Linux x86_64/aarch64 (glibc 2.34+). Release verification records exact wheel hashes and independent installed-wheel recovery.
Deployment and evidence
Graviton5 deployment instructions and
current provisioning status describe the separate
c9g.8xlarge parent, scoped instance role, reproducible ARM build procedure and
hardware gate. The gate verifies local V3 MIDRs, local NSM PCR4, and a live signed
DescribeInstances request over authenticated TLS to AWS. It rejects stale IID
substitution and wrong instance type. AWS Nitro and its attestation PKI remain
trusted.
Pinned native implementation, Python client, independent reviews and triage and automated tests are available in the source tree. Reviews identify questions and defects to resolve; they are not certifications. Production measurements and warm-up evidence record the exact source, matching PCRs and verified diagnostic requests. Public Cloudflare transport is verified. Full-work completion and daily rollover remain pending. The new eight-worker follower awaits the new production puzzle while older recovery jobs continue. Two artifact replicas are operating; a third provider remains pending. Fleet deployment.
Public frozen-source downloads include the
frozen source releases, a minimal rebuildable checkout, and its PCR/build
evidence. The earlier 474083e release remains available.
All published files passed anonymous HTTPS readback and full-byte hash checks.
The threat model explicitly trusts AWS while treating the
service operator as potentially malicious. Public CI reproduction
adds two GitHub-hosted builds and a separate measurement/attestation job for
clients that can verify cryptographic evidence but cannot compile the application.
The public run
passed: both hosted builds reproduced production's Docker image and PCR0/1/2,
and a separate job remeasured and signed the artifacts. Download the evidence,
pin reviewed CI revision a0fad3334b501c610c20dbe27137edaa607c90b3, verify its
signed bundle, then verify the live Nitro/TLS binding. Recorded verification
includes rejection of an altered report and an incorrect workflow revision.