PinionAI .AIA MIME TYPE
This document provides details about the .AIA MIME Specification format
1. Byte layout (raw, before Base64 encoding)
- salt: 16 bytes (PBKDF2 salt) -> offset 0..15
- nonce (IV): 12 bytes (GCM nonce) -> offset 16..27
- ciphertext: N bytes (variable) -> offset 28..(len-17)
- tag: 16 bytes (GCM authentication tag) -> trailing 16 bytes
2. Encoded representation:
- The final bytes (salt || nonce || ciphertext || tag) are encoded with base64.urlsafe_b64encode and stored/sent as ASCII. Example file contents are the base64 string.
Key derivation:
- PBKDF2-HMAC-SHA256
- salt length: 16 bytes
- iterations: 390000
- derived key length: 32 bytes (256 bits)
- Pseudocode: key = PBKDF2_HMAC_SHA256(password=key_secret_bytes, salt=salt, iterations=390000, dklen=32)
3. Encryption algorithm:
- AES-256-GCM
- Nonce length: 12 bytes (96 bits)
- Tag length: 16 bytes (128 bits)
- Associated data: none (not used)
- Pseudocode encrypt: salt = random_bytes(16) nonce = random_bytes(12) key = PBKDF2(... as above ...) encryptor = AESGCM(key) ciphertext, tag = encryptor.encrypt_and_digest(plaintext_bytes, nonce) out = salt || nonce || ciphertext || tag encoded = base64.urlsafe_b64encode(out)
- Prefix encryption package with: aia_{version}_{keyId}_{datetimeiso}_ (ex. aia_version1_7c08a121-70a0-42f0-b540-b2315069aef0_2025-11-04T15:05:55.302346Z_*)
- To correctly format the timestamp, use a timezone-aware datetime object and adhere to the RFC3339 "Zulu time" format
4. Decryption algorithm:
Steps to decrypt a .aia file:
- Detect and strip optional aia v1 prefix: "aia_{version}_{keyId}_{datetimeiso}_
" - If present, extract version, keyId and datetimeiso from the prefix for use with acquiring decryption key.
- base64 decode the file content using URL-safe base64.
- Validate total length >= 16 (salt) + 12 (nonce) + 16 (tag) + 1 (min ciphertext). Reject otherwise.
- Extract salt = bytes[0:16]; nonce = bytes[16:28]; tag = bytes[-16:]; ciphertext = bytes[28:-16]
- key = PBKDF2_HMAC_SHA256(password=key_secret_bytes, salt=salt, iterations=390000, dklen=32)
- decryptor = AESGCM(key); plaintext_bytes = decryptor.decrypt(nonce, ciphertext + tag, associated_data=None)
- Decode plaintext_bytes as UTF-8 text; parse as JSON.
Pseudocode decrypt:
- raw = base64.urlsafe_b64decode(encoded_string)
- salt = raw[0:16]; nonce = raw[16:28]; tag = raw[-16:]
- ciphertext = raw[28:-16]
- key = PBKDF2_HMAC_SHA256(key_secret, salt, iterations=390000, dklen=32)
- plaintext = AESGCM(key).decrypt(nonce, ciphertext + tag, None)
- json_obj = JSON.parse(plaintext.decode('utf-8'))
5. Reference implementation notes
- Salt constant: 16 bytes
- Nonce constant: 12 bytes
- Tag: 16 bytes
- PBKDF2 iterations: 390000
- Cipher: AES-GCM, 256-bit key
- Encoding: base64.urlsafe_b64encode of concatenated bytes
6. Interoperability considerations:
- Implementations MUST support the exact parameters above to maintain interoperability.
- Implementations MAY support alternate KDF iteration counts via an envelope metadata approach, but any change MUST be defensibly recorded in file metadata (not currently present in the base format).
- When reading older files encrypted with different parameters, the decryptor MUST be able to be configured to derived versioned decryptor key secret with the key Id, version name, datetime parameters or the file MUST include an explicit metadata header (future version) where this information can be located.
- Current format includes no version header; instead, prepend a small version prefix before salt in a future revision and increment media type versioning.
- If there is no encrypted payload, the file is considered a shortcut reference.
7. Applications that use this media type:
- Encrypted PinionAI agent configuration bundles, agent templates, or secrets-containing agent files.
- Transport of agent packages between systems that share the key_secret to decrypt.
8. Example (decrypted content example)
{
"agent": {
"apis": [
{
"url": "http://localhost:8080/customer",
"body": "{\"email\": \"{var[\\\"email\\\"]}\", \"phone\": \"{var[\\\"phone_number\\\"]}\", \"lastName\": \"{var[\\\"last_name\\\"]}\", \"firstName\": \"{var[\\\"first_name\\\"]}\",\"password\":\"{var[\\\"password\\\"]}\"}",
"name": "new customer",
"header": "{\"accept\": \"*/*\"}",
"method": "POST",
"results": null,
"connector": "myAPIService",
"content_type": "application/json",
"resultVariable": "api_resp",
"failureResponseMessage": "New record failed to save. Please contact your administrator."
},
...
],
"name": "Stock Evaluator",
"files": [...],
"stores": [],
...
}
}
Example pseudocode (Python-like) matching an EncryptionManager example
# Encrypt (producer)
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os, base64
# parameters
SALT_LEN = 16
NONCE_LEN = 12
TAG_LEN = 16
ITERATIONS = 390000
KEY_LEN = 32 # AES-256
def encrypt(key_secret: str, plaintext: bytes) -> str:
salt = os.urandom(SALT_LEN)
nonce = os.urandom(NONCE_LEN)
key = PBKDF2HMAC(algorithm=hashes.SHA256(), length=KEY_LEN, salt=salt, iterations=ITERATIONS).derive(key_secret.encode())
aesgcm = AESGCM(key)
ct = aesgcm.encrypt(nonce, plaintext, associated_data=None) # returns ciphertext || tag
out = salt + nonce + ct
return base64.urlsafe_b64encode(out).decode('ascii')
def decrypt(key_secret: str, encoded_b64: str) -> bytes:
raw = base64.urlsafe_b64decode(encoded_b64)
if len(raw) < SALT_LEN + NONCE_LEN + TAG_LEN + 1:
raise ValueError("Invalid input length")
salt = raw[0:SALT_LEN]
nonce = raw[SALT_LEN:SALT_LEN+NONCE_LEN]
ct_and_tag = raw[SALT_LEN+NONCE_LEN:]
key = PBKDF2HMAC(algorithm=hashes.SHA256(), length=KEY_LEN, salt=salt, iterations=ITERATIONS).derive(key_secret.encode())
aesgcm = AESGCM(key)
plaintext = aesgcm.decrypt(nonce, ct_and_tag, associated_data=None)
return plaintext
```
9. Shortcut potential:
- In some cases a shortcut without encrypted payload can be allowed. In this case a shortcut will not contain the payload, but only the prefix.
- Prefix encryption package with: aia_{version}_{keyId}_{datetimeiso}_ (ex. aia_version1_7c08a121-70a0-42f0-b540-b2315069aef0_2025-11-04T15:05:55.302346Z_)