Skip to content

Large-File Encryption (Streaming Mode, JWET)

The Secure Streaming /encrypt endpoint turns multi-gigabyte payloads into JWET ciphertext in one pass.
Neither client nor server ever buffers the whole file; each block is encrypted on arrival and immediately streamed out.

This is now true of the disk as well as the heap

Until recently the server kept its side of that promise in memory only: the servlet container wrote every uploaded part to a temporary file before the handler ran, so a 5 GB upload became a 5 GB file on the server's disk while the heap stayed flat. That materialisation is gone. The payload is now read straight from the socket into the cipher — zero bytes written to disk — measured at 512 MiB through a JVM capped at 256 MB of heap, with no temporary file created at any point.


1 · Process overview

Step Action Endpoint Payload format
1 Ensure key exists (AES, RSA, ML-KEM, …) /api/key-management/keys JSON
2 Stream-encrypt file → JWET /api/v3/crypto/stream/encrypt multipart/form-data

2 · Why streaming encryption matters

  • Memory-safe – constant RAM regardless of file size.
  • Secure by design – plaintext never touches disk.
  • Scalable – encrypt terabytes without tuning JVM heap / OS buffers.
  • Fast – crypto starts with the first 8 kB, not after EOF.

3 · High-level flow

flowchart TD
    A["Client"] -->|multipart/form-data| B(API /encrypt •stream)
    B -->|read chunk| C["Encrypt block in memory"]
    C -->|write JWET chunk| D["Encrypted output stream"]
    D --> B
    B --> E(("Done"))

4 - Endpoint details

4.1 Generate / retrieve encryption key

POST /api/key-management/keys
{
  "kid": "mySymKey",
  "kty": "oct",
  "alg": "AES-256",
  "keyOps": ["encrypt","decrypt"],
  "exportable": false
}

Response201 Created

For asymmetric encryption replace kty/alg with e.g. "kty":"ML-KEM","alg":"ML-KEM-1024".


4.2 Stream-encrypt

  • EndpointPOST /api/v3/crypto/stream/encrypt

  • Bodymultipart/form-data

Part Name Content-Type Notes
1 metadata application/json EncryptStreamRequest (see § 4.2.1)
2 file application/octet-stream Raw plaintext stream

The order of these two parts is required, not conventional

metadata must be written first. The server reads the body in a single pass and streams file straight into the cipher, so it cannot go back for a metadata part that arrives after the payload — and it has no way to tell that case apart from one where you never sent it. Either shape is answered 400, type https://docs.ankatech.co/errors/multipart-part-order, before a single payload byte is read.

Most HTTP client libraries preserve the order in which you add parts, so this costs you nothing as long as you add metadata before file. The official Java SDK does it for you.

4.2.1 metadata part anatomy (EncryptStreamRequest)

Field Type Required Description
kid string Yes Key id used to wrap the CEK (mySymKey, myRsaKid, ...).

The server selects the alg/enc pair based on the key's policy (e.g. dir+A256GCM, RSA-OAEP-256+A256GCM, ML-KEM-1024+A256GCM).

Example
{ "kid": "myRsaKid" }

4.3 Sample request / response (RSA-OAEP-256 → JWET)

Request

POST /api/v3/crypto/stream/encrypt HTTP/1.1
Host: api.ankasecure.co
Authorization: Bearer <TOKEN>
Content-Type: multipart/form-data; boundary=---ankasecure-b9c1

---ankasecure-b9c1
Content-Disposition: form-data; name="metadata"
Content-Type: application/json

{ "kid":"myRsaKid" }
---ankasecure-b9c1
Content-Disposition: form-data; name="file"; filename="largeFile.bin"
Content-Type: application/octet-stream

<PLAINTEXT_STREAM>
---ankasecure-b9c1--`

Response 200 OK (multipart/mixed)

X-Key-Requested: myRsaKid
X-Key-Used: myRsaKid
X-Algorithm-Used: RSA-OAEP-256+A256GCM
Transfer-Encoding: chunked
Content-Type: multipart/mixed; boundary=ankatech-d6f3d869`
--ankatech-d6f3d869
Content-Type: application/jose+json

{"protected":"eyJhbGciOiJSQS1PQUVQLTI1NiIsImVuYyI6IkEyNTZHQ00iLCJraWQiOiJteVJzYUt pZCJ9",
"iv":"EfLIKHTs1814g1B3",
"recipients":[
{"header":{"alg":"RSA-OAEP-256","kid":"myRsaKid"},
"encrypted_key":"FJEW8MVEq6KdJgCe..."}
]}
--ankatech-d6f3d869
Content-Type: application/octet-stream

< BINARY  ⟨ciphertext+tag⟩  STREAM >
--ankatech-d6f3d869--`

5 - Example workflow (CLI)

# 1. Ensure the key exists
curl -s -o /dev/null -w '%{http_code}\n'\
-H "Authorization: Bearer $TOKEN"\
https://api.ankasecure.co/api/key-management/keys/myRsaKid\
|| curl -X POST https://api.ankasecure.co/api/key-management/keys\
-H "Authorization: Bearer $TOKEN"\
-H "Content-Type: application/json"\
-d '{"kid":"myRsaKid","kty":"RSA","alg":"RSA-2048","keyOps":["encrypt","decrypt"]}'

# 2. Stream-encrypt a 10 GB video
curl -X POST https://api.ankasecure.co/api/v3/crypto/stream/encrypt\
-H "Authorization: Bearer $TOKEN"\
-F "metadata={\"kid\":\"myRsaKid\"};type=application/json"\
-F "[email protected];type=application/octet-stream"\
--output movie.jwet

6 - Key advantages

  1. Constant memory -- CPU and network I/O scale, RAM stays flat.

  2. Inline security -- ciphertext leaves the JVM already encrypted.

  3. Algorithm agility -- swap kid to move from RSA → ML-KEM with no code change.

  4. Lifecycle telemetry -- headers expose the exact key and algorithm in use.


Document version 3.0.0 -- generated from OpenAPI build December 2025

© 2025 ANKATech Solutions INC. All rights reserved.