Skip to content

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
Decoded (pretty-printed):
{
"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)

POST /api/crypto/stream/encrypt
Authorization: Bearer <JWT>
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

{ "kid": "sc1_kem_1749774551152" }
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-...--
**Header**
Crypto-Policy-Info: eyJjdXJyZW50X2tleV9yZXF1ZXN0ZWQiOiJzYzFfa2VtXzE3NDk3NzQ1NTExNTIiLCJjdXJyZW50X2tleV91c2VkIjoi... (Base64URL)

5.2 Encrypt (stream) --- symmetric (alg=dir)

meta.json

{ "kid": "sc3_aes256_1749774556956" }
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)

POST /api/crypto/stream/sign
Authorization: Bearer <JWT>

meta.json

{ "kid": "sc2_rsa_1749774554621" }
200 OK (detached JWS -- truncated)
{
  "protected":"eyJhbGciOiJQUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il0sImtpZCI6InNjMl9yc2FfMTc0OTc3NDU1NDYyMSJ9",
  "signature":"RNxXG8RoXKQq2pPmdpSxYy..."
}
**Header**
Crypto-Policy-Info: eyJjdXJyZW50X2tleV9yZXF1ZXN0ZWQiOiJzYzJfcnNhXzE3NDk3NzQ1NTQ2MjEiLCJjdXJyZW50X2tleV91c2VkIjoi... (Base64URL)

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)
Crypto-Policy-Info: eyJjdXJyZW50X2tleV9yZXF1ZXN0ZWQiOiJvbGRSc2FLaWQiLCJuZXdfa2V5X3JlcXVlc3RlZCI6InBxaC1tbGtlbS03NjgiLCJtaWdyYXRpb25fbW9kZSI6dHJ1ZSwgLi4u (Base64URL)
Body: `multipart/mixed` JWET wrapped with **ML-KEM-768**.

5.5 Re-sign (stream) --- PS256 → Falcon-512

POST /api/crypto/stream/resign
Authorization: Bearer <JWT>

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

  1. Stream, don't buffer -- feed data as an InputStream; memory stays flat.

  2. Watch the Crypto-Policy-Info header -- rotate keys before the next TB-scale batch.

  3. Chunk uploads if a single file nears 5 MB to avoid 413.

  4. 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.