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
{
"kid": "mySymKey",
"kty": "oct",
"alg": "AES-256",
"keyOps": ["encrypt","decrypt"],
"exportable": false
}
Response 201 Created
For asymmetric encryption replace
kty/algwith e.g."kty":"ML-KEM","alg":"ML-KEM-1024".
4.2 Stream-encrypt
-
Endpoint
POST /api/v3/crypto/stream/encrypt -
Body
multipart/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/encpair based on the key's policy (e.g.dir+A256GCM,RSA-OAEP-256+A256GCM,ML-KEM-1024+A256GCM).
Example
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
-
Constant memory -- CPU and network I/O scale, RAM stays flat.
-
Inline security -- ciphertext leaves the JVM already encrypted.
-
Algorithm agility -- swap
kidto move from RSA → ML-KEM with no code change. -
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.