Sign every AI answer. Even the ones that stream in live.
AFiR-Stream signs each piece of a live call in 11.59µs, measured against a 100µs budget, so the call never waits on the signing. When the call ends, it locks the whole session with a post-quantum ML-DSA-65 signature. It has five layers of protection built for HIPAA voice calls, PCI call centers, and AI agents that run for hours. You can check it offline on a laptop. No secret key needed.
Voice calls and live AI streams broke every receipt design that came before this.
A three-minute HIPAA voice call can be hundreds of model turns. Older designs made you pick: fast calls, catching tampering, or letting auditors check things offline. You couldn't get all three at once.
Sign every turn as it happens
This adds milliseconds to every piece of the call. That eats up the whole speed budget before the first packet even ships. The voice call gets choppy, and customers move to a faster vendor.
Sign once, only at the end
This is fast. But if someone tampers with the middle of the call, you won't know until the call ends, and if they also tamper with the ending, you may never know at all. There's no way to catch tampering mid-call.
Check receipts online, through us
Here the auditor has to call your service to check a receipt. Now the audit depends on the vendor being up and cooperative. That's not how compliance is supposed to work. PCI and HIPAA reviewers need to check things on their own computers.
One fast lane for the call. One side lane for signing. One lock at the end.
The call itself never waits on signing. The signing sidecar never slows the call down. The post-quantum lock never touches the fast lane. Three lanes working together, and you get one proof you can check.
HOT PATH OFF PATH AT SESSION CLOSE -------- -------- ---------------- producer --> enqueue(seg) ----+ ( voice / LLM stream ) | v +---------------------+ +-----------------------+ | sidecar queue | ---> | per-segment receipt | | (non-blocking) | | Ed25519, off-path | +---------------------+ +-----------------------+ | v chain of segment receipts ( hash- linked, tamper- detectable ) | v +-------------------+ | ML-DSA-65 anchor | <-- session.close() | PQ-finalized | +-------------------+ | v one verifiable proof ( offline, zero-secret )
Each layer makes the receipt harder to fake and easier to check.
We built these in the order D, C, A, B, E, but you can read them in any order. Each one works on its own. All five are tied into the same signature chain, so tampering with any of them breaks the check.
Dropped calls, reconnects, and transfers stay one record.
A dropped connection, a network change, or a handoff to a human agent never breaks the chain. The next piece of the call after the gap carries a block that links back to both the session and the last piece signed before the gap.
- sidecar.mark_interruption(cause=transport_close|network_change|transfer)
- sidecar.mark_transfer(to_handler=did:...) for warm-handoff scenarios
- verify_chain accepts seq jumps iff a continuation block is present
- missing continuation block → chain rejected as incomplete
Every piece of the call says whether the AI or the other person said it.
source_role is required on every piece of the call. It's locked into that piece's signature, so changing who said what breaks the check. The asserts field spells out the limit plainly: this only shows where the words came from in the session. It does not prove the other person's real-world identity.
- source_role: "model_side" | "counterparty_side" (required field)
- asserts: stream_provenance_only; source_role_is_origin_not_identity
- tamper test: flipping source_role on one segment fails offline verify
- no overclaiming: the receipt never claims to know who the other person actually is
The "barely any delay" claim is measured and signed, not just stated.
The sidecar times its own added delay on every call and locks {max, p99, mean, declared_budget_us, within_budget, n_samples} into the SessionReceipt. This timing proof is part of the post-quantum signature. Change any of those numbers and the signature check fails.
- measured max in smoke test: 11.59 µs against a 100 µs declared budget
- within_budget = (max ≤ declared_budget_us); this is reported, not just claimed
- changing declared_budget_us after the fact breaks the ML-DSA-65 check
- n_samples is reported so the auditor can see how many samples this covers
You can tell a live receipt from a locked one just by looking.
Every receipt for a single piece of the call carries verification_state="live_provisional" and sig_alg="ed25519". The final SessionReceipt carries verification_state="sealed" and sig_alg="ML-DSA-65". Nobody, human or system, can mistake a mid-call receipt for the final locked one.
- segment receipts: live_provisional / Ed25519 (fast, off the main path)
- session receipt: sealed / ML-DSA-65 (post-quantum)
- the checker automatically picks the right method based on the state field
- there is no way to pass off a live receipt as a sealed one
Card numbers and health data never touch the receipt. The receipt still proves they were there.
A card number, a social security number, or a medical record value gets blacked out before that piece of the call is signed. Only {pos, class, commit, in_boundary} go into the receipt. The real value never enters the chain. Later, an authorized auditor who has the secret key and the value can prove the hidden value matches what was recorded, without anyone else ever seeing it.
- redact_text(cleartext, spans) → (redacted_text, span_dicts, salts)
- commit = SHA-256(domain || salt || class || value): this cannot be reversed
- verify_disclosure(span, salt, value) is the check that reveals a value only to someone authorized
- the wrong value fails, the wrong secret key fails, the wrong data class fails
Every check passed. Every number measured. Tampering gets caught.
Both test suites run from a fresh checkout: five basic checks, five checks on the extra layers of protection. All ten passed. We measured the extra delay on a 200-segment stream running at a 300ms pace.
Acceptance checks 10 of 10 pass
- 1✓Almost no added delay on the live call
- 2✓The session receipt can be rebuilt from the chain of pieces
- 3✓The whole chain can be checked offline with no secret key
- 4✓Tampering with one piece breaks the whole chain
- 5✓The post-quantum lock at the end checks out offline
- 6✓Layer D: live pieces look different from the sealed session
- 7✓Layer C: the speed proof stays inside budget and is part of the final signature
- 8✓Layer A: a drop and reconnect still checks out end to end
- 9✓Layer B: who said what is locked in, and tampering breaks the check
- 10✓Layer E: protected data never shows up in the receipt, and the hidden match still checks out
Speed test and timing proof
200 pieces at a 300ms pace. The side-path signing held up the whole way through. These numbers ship with every session receipt as signed proof.
Wrap your stream. Get receipts. Check them offline.
The sidecar sits right next to whatever is producing your stream: an AI model, a voice system, an agent loop, anything. You call enqueue() for each piece and it returns right away. When the session closes, you get back one receipt locked with a post-quantum signature. Anyone with the public keys can check the whole chain offline.
from hive_stream import StreamSidecar, SegmentInput, Grounding, LiveSigner, PQFinalizer from hive_stream.boundary import ComplianceBoundary from hive_stream.redaction import redact_text # 1. open a sidecar (everything after this runs on the fast path; this line runs once per session) sidecar = StreamSidecar( session_id="did:hive:tenant:my-call-center", model_ref="mir:hivecompute/voice-pci-v1", live=LiveSigner.from_key(LIVE_ED25519_KEY), pq=PQFinalizer.from_key(PQ_MLDSA_KEY), declared_budget_us=100, ).start() # 2. for each piece of the live stream: this is the fast path for seg in voice_stream: redacted_input, spans_in, salts_in = redact_text(seg.heard, seg.pii_spans_in) redacted_output, spans_out, salts_out = redact_text(seg.said, seg.pii_spans_out) sidecar.enqueue(SegmentInput( seq=seg.i, t_start=seg.t0, t_end=seg.t1, input_window=redacted_input, output=redacted_output, model_ref="mir:hivecompute/voice-pci-v1", grounding=Grounding(score=seg.grounding_score, claims_root=seg.claim_hash), boundary=ComplianceBoundary(in_scope=True, pii_class="phi"), source_role="model_side", # Layer B redacted_spans=spans_in + spans_out, # Layer E )) # enqueue() returns right away. signing happens on the side path, not the fast one. # 3. mid-stream interruption? just one line. (Layer A) if transport.dropped(): sidecar.mark_interruption(cause="transport_close") # 4. close the session: this locks it with an ML-DSA-65 post-quantum signature session_receipt = sidecar.close_session() # session_receipt.verification_state == "sealed" (Layer D) # session_receipt.latency_attestation (Layer C) # check it offline, anywhere: # from hive_stream.session import verify_chain # ok, why = verify_chain(sidecar.chain, LIVE_PUBLIC_KEY)
Built for calls that need to be auditable the next business day.
Anywhere an AI model is talking with a person about something regulated.
HIPAA voice calls
Telehealth intake, behavioral health sessions, voice agents that touch health records. Layer E keeps a record of what kind of data was there. The data itself stays out.
PCI call centers
Card-not-present voice calls where card numbers need to be auditable without ever landing in a log file. Your PCI scope shrinks while your auditability grows.
AI agents running for hours
Long-running agent loops where a model talks to other models or tools for hours at a time. Layer A means transfers and reconnects stay part of one record.
AI proxies for regulated industries
If you resell AI access to banks, insurers, or hospitals, AFiR-Stream is the receipt layer your customers' auditors will ask for first.
The signer is live right now. This isn't a slide.
The AFiR-Stream signer runs in production today at receipts.thehiveryiq.com. It signs a piece of the call off the fast path, locks the session when it closes, and checks the chain. Every response is a real Ed25519-signed envelope, and the sealed session shows its ML-DSA-65 post-quantum lock.
curl -sS https://receipts.thehiveryiq.com/v1/afir-stream/examples | python3 -m json.tool
Measured, not marketed. The example session locks seven pieces with a worst-case signing time of about 12µs against a declared 100µs budget. The fast path never gets blocked. These timing numbers are a single-client liveness check, not a production guarantee.
smoke/smoke_afir_stream.py) and the bench test (bench/bench_afir_stream.py) checked into the AFiR-Stream code. The receipt schema and offline checker ship with your API key. SiGR, MiR, GCA, GiTM, QPuF, AFiR-CacheSign, and AFiR-Manifest are live at /.