Secure Streaming (Detached JWS & JWET)
High-throughput endpoints for payloads processed as byte streams
1 · Why do we need streaming endpoints?
The non-streaming Secure APIs cap decoded payloads at 5 MB.
For a 40 GB DB dump or a 4 K video, the buffer-the-whole-thing model fails:
- Memory blow-up – Base64 inflation (≈ 33 %) + app buffers.
- Latency – crypto starts only after the final byte.
- Gateway time-outs – idle sockets drop.
Secure Streaming fixes this: client and server treat the payload as a
stream; cryptography occurs on-the-fly; lifecycle data travels in the
Crypto-Policy-Info
response header.
2 · Endpoint catalogue (tag Secure Streaming
)
Verb | Path | Function | metadata schema |
Response body | Typical use |
---|---|---|---|---|---|
POST | /api/crypto/stream/encrypt |
Encrypt → JWET | EncryptStreamRequest |
multipart/mixed |
Video vault backup |
POST | /api/crypto/stream/decrypt |
Decrypt JWET | JwetGeneralJsonHeaderDecrypt |
application/octet-stream |
Big-data ETL |
POST | /api/crypto/stream/sign |
Sign → detached-JWS | SignStreamRequest |
application/octet-stream |
Audit logs |
POST | /api/crypto/stream/verify |
Verify detached-JWS | VerifyDetachedJwsStreamRequest |
JSON | Compliance |
POST | /api/crypto/stream/reencrypt |
Decrypt w/ currentKid, encrypt w/ newKid | JwetGeneralJsonHeaderReencrypt + query params |
multipart/mixed |
RSA → ML-KEM migration |
POST | /api/crypto/stream/resign |
Verify w/ currentKid, sign w/ newKid | ResignDetachedJwsStreamRequest |
application/octet-stream |
PS256 → Falcon-512 |
3 · Request anatomy
Each call is a two-part multipart/form-data
stream:
--boundary
Content-Disposition: form-data; name="metadata"; filename="meta.json"
Content-Type: application/json
{ ... }
--boundary
Content-Disposition: form-data; name="file"; filename="payload.bin"
Content-Type: application/octet-stream
<binary stream>
--boundary--
4 · Response header (key-lifecycle telemetry)
Header name | Type | Purpose |
---|---|---|
Crypto-Policy-Info |
string (Base64URL) |
Encodes a JSON document that summarises the lifecycle status of the current key and, on dual-key flows, the new key. Clients must Base64URL-decode the value and deserialise it into a CryptoPolicyInfo DTO. |
4.1 JSON schema (snake_case)
Field | Description |
---|---|
current_key_requested |
kid requested by the caller. |
current_key_used |
Effective kid after rotation lookup. |
current_key_algorithm |
Algorithm used (e.g. ML-KEM-768+A256GCM ). |
current_key_exp_warn_days |
Days until hard expiry (0 = none). |
current_key_usage_warn |
Invocations until hard usage limit (0 = none). |
new_key_requested |
kid supplied via newKid (dual-key flows only). |
new_key_used |
Effective kid used for encryption / signing. |
new_key_algorithm |
Algorithm applied to the successor key. |
new_key_exp_warn_days |
Days until hard expiry of the new key (0 = none). |
new_key_usage_warn |
Invocations until hard usage limit on the new key (0 = none). |
migration_mode |
true when sourceKidOverride was required because the incoming header lacked a kid . |
4.2 Example header
Sample value
Crypto-Policy-Info: eyJjdXJyZW50X2tleV9yZXF1ZXN0ZWQiOiJzYzRfcnNhXzE3NTA2OTQ2NTEzMDEiLCJjdXJyZW50X2tleV91c2VkIjoic2M0X3JzYV8xNzUwNjk0NjUxMzAxIiwiY3VycmVudF9rZXlfYWxnb3JpdGhtIjoiUlNBLTIwNDgiLCJjdXJyZW50X2tleV9leHBfd2Fybl9kYXlzIjowLCJjdXJyZW50X2tleV91c2FnZV93YXJuIjowLCJuZXdfa2V5X3JlcXVlc3RlZCI6InNjNF9rZW1fMTc1MDY5NDY1MjAzNyIsIm5ld19rZXlfdXNlZCI6InNjNF9rZW1fMTc1MDY5NDY1MjAzNyIsIm5ld19rZXlfYWxnb3JpdGhtIjoiTUwtS0VNLTEwMjQiLCJuZXdfa2V5X2V4cF93YXJuX2RheXMiOjAsIm5ld19rZXlfdXNhZ2Vfd2FybiI6MCwibWlncmF0aW9uX21vZGUiOmZhbHNlfQ
{
"current_key_requested" : "sc4_rsa_1750694651301",
"current_key_used" : "sc4_rsa_1750694651301",
"current_key_algorithm" : "RSA-2048",
"current_key_exp_warn_days": 0,
"current_key_usage_warn" : 0,
"new_key_requested" : "sc4_kem_1750694652037",
"new_key_used" : "sc4_kem_1750694652037",
"new_key_algorithm" : "ML-KEM-1024",
"new_key_exp_warn_days" : 0,
"new_key_usage_warn" : 0,
"migration_mode" : false
}
5 - Endpoint walkthroughs
5.1 Encrypt (stream) --- asymmetric (ML-KEM)
curl -X POST\
-H "Authorization: Bearer $TOKEN"\
-F "[email protected];type=application/json"\
-F "[email protected];type=application/octet-stream"\
https://api.ankasecure.co/api/crypto/stream/encrypt
meta.json
200 OK (multipart/mixed -- truncated)
------ankatech-5e79dfb7-...
Content-Type: application/jose+json
{"protected":"eyJhbGciOiJNTC1LRU0tNTEyIiwiZW5jIjoiQTI1NkdDTSIsImtpZCI6InNjMV9rZW1fMTc0OTc3NDU1MTE1MiJ9",
"iv":"QE-6gFcGy4B9yaQw",
"recipients":[{"header":{"alg":"ML-KEM-512","kid":"sc1_kem_1749..."},
"encrypted_key":"jrM_6C5x..."}]}
------ankatech-5e79dfb7-...
Content-Type: application/octet-stream
<binary envelope + ciphertext + tag>
------ankatech-5e79dfb7-...--
5.2 Encrypt (stream) --- symmetric (alg=dir
)
meta.json
200 OK (multipart/mixed -- direct AES-GCM)
------ankatech-cfd64938-...
Content-Type: application/jose+json
{"protected":"eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoic2MzX2FlczI1Nl8xNzQ5Nzc0NTU2OTU2In0",
"iv":"Fq_odhuz_50HZBW-",
"recipients":[{"header":{"alg":"dir","kid":"sc3_aes256_1749..."}, "encrypted_key":""}]}
------ankatech-cfd64938-...
Content-Type: application/octet-stream
<binary envelope + ciphertext + tag>
------ankatech-cfd64938-...--
5.3 Sign (stream)
meta.json
200 OK (detached JWS -- truncated)
**Header**5.4 Re-encrypt (stream) --- RSA → ML-KEM
POST /api/crypto/stream/reencrypt?newKid=pqk-mlkem-768&sourceKidOverride=oldRsaKid
Authorization: Bearer <JWT>
200 OK (dual-key -- truncated)
Body: `multipart/mixed` JWET wrapped with **ML-KEM-768**.5.5 Re-sign (stream) --- PS256 → Falcon-512
meta.json
{
"oldJws" : "<detached_header_ps256>",
"newKid" : "pqk-falcon-512",
"sourceKidOverride": "oldRsaKid" // optional
}
Response: detached-JWS header + signature.\
Crypto-Policy-Info
encodes both key halves and, if applicable, "migration_mode": true
.
!!! tip "Ensure your client really streams"
Runtime | How to stream |
---|---|
cURL | -F file=@- or --data-binary @- |
Java | Pass an InputStream to the HTTP client and call setChunkedStreamingMode(8 * 1024) |
Node.js | Pipe fs.createReadStream() into fetch() / Axios |
Disable HTTP-client automatic retries; the CLI / SDK handle resumable uploads via Range requests.
6 - Error reference (RFC 7807)
HTTP | type URI |
Typical trigger |
---|---|---|
400 | /errors/invalid-input |
Missing file part, malformed JSON, bad Base64. |
401 | /errors/unauthorized |
Expired or invalid JWT. |
404 | /errors/not-found |
kid unknown or unsuitable. |
409 | /errors/conflict |
Key revoked, suspended, algorithm mismatch. |
413 | /errors/payload-too-large |
Any part > 5 MB. |
500 | /errors/internal |
Unexpected crypto failure. |
Problem documents include timestamp
, detail
, instance
, and honour Accept-Language
.
7 - Best practices
-
Stream, don't buffer -- feed data as an
InputStream
; memory stays flat. -
Watch the
Crypto-Policy-Info
header -- rotate keys before the next TB-scale batch. -
Chunk uploads if a single file nears 5 MB to avoid 413.
-
Automate yearly re-encrypt / re-sign to stay ahead of cryptanalytic advances.
Document version 3.0 -- generated from OpenAPI build 2025-06-23
© 2025 AnkaTech Co. All rights reserved.