README.md
On this page

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.