Skip to content

End-to-End Encryption

PushGo E2EE protects sensitive business fields. The sender encrypts fields into ciphertext, the Gateway relays that ciphertext, and clients decrypt locally after a key is configured.

ContentProtected by E2EENotes
Business fields inside ciphertextYesThe Gateway cannot read plaintext.
Plain request fieldsNochannel_id, password, severity, and route IDs are normal request fields.
HTTP headersNoProtected by HTTPS/TLS, not E2EE.
Delivery metadataNoThe Gateway still needs basic metadata for auth, routing, and dispatch.

E2EE is not a replacement for Gateway authentication. You still need channel credentials, optional Gateway Bearer tokens, and HTTPS.

PushGo clients support AES-GCM. The key length determines the AES variant.

Key lengthAlgorithmNonce / IVAuth tag
16 bytesAES-128-GCM12 bytes16 bytes
24 bytesAES-192-GCM12 bytes16 bytes
32 bytesAES-256-GCM12 bytes16 bytes

Binary layout:

[ ciphertext (N bytes) ][ auth tag (16 bytes) ][ nonce / iv (12 bytes) ]

Base64-encode the full binary blob and place it in the API ciphertext field.

After decryption, ciphertext must contain a JSON object. Clients recognize the following canonical fields and write them back into the notification payload.

FieldTypeBehavior
titlestringOverrides notification title.
bodystringOverrides message body.
urlstringOverrides click-through URL.
imagesstring[] or JSON stringOverrides image list.
tagsstring[] or JSON stringOverrides tag list.
metadataobject or JSON stringOverrides metadata.
FieldTypeBehavior
descriptionstringOverrides event description.
statusstringOverrides event status.
messagestringOverrides event message.
started_atnumberOverrides event start time.
ended_atnumberOverrides event end time.
attrsobject or JSON stringOverrides event attrs patch.
FieldTypeBehavior
primary_imagestringOverrides primary image.
statestringOverrides entity state.
created_atnumberOverrides creation time.
deleted_atnumberOverrides deletion time.
external_idsobject or JSON stringOverrides external IDs.
location_typestringOverrides location type.
location_valuestringOverrides location value.
locationobject or JSON stringOverrides location object.
import base64
import json
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
def encrypt_payload(key_hex, payload):
key = bytes.fromhex(key_hex)
aesgcm = AESGCM(key)
nonce = os.urandom(12)
plaintext = json.dumps(payload, separators=(",", ":")).encode("utf-8")
cipher_and_tag = aesgcm.encrypt(nonce, plaintext, None)
return base64.b64encode(cipher_and_tag + nonce).decode("utf-8")
payload = {
"title": "Database lag",
"body": "Replica lag exceeded 60 seconds.",
"tags": ["encrypted", "database"],
"metadata": {"source": "replica-monitor"}
}
key_hex = "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
print(encrypt_payload(key_hex, payload))

The example key_hex is a 32-byte key for AES-256-GCM. Use a securely generated random key in production, and configure the same key locally in the client.

Terminal window
curl -X POST https://gateway.pushgo.dev/message \
-H "Content-Type: application/json" \
-d '{
"channel_id": "YOUR_CHANNEL_ID",
"password": "YOUR_CHANNEL_PASSWORD",
"title": "Encrypted message",
"body": "Your client will try to decrypt ciphertext.",
"severity": "normal",
"ciphertext": "BASE64_ENCODED_CIPHERTEXT"
}'

Plain title and body can be fallback display for clients without a key or when decryption fails. Put truly sensitive content inside ciphertext.

StateMeaning
decryptOkDecryption succeeded and at least one field was applied.
decryptFailedCiphertext exists, but decrypting or parsing failed.
notConfiguredThe client has no usable key configured.
algMismatchThe configured algorithm does not match the payload.
ProblemCheck
Decryption failsSame key, complete Base64, and binary layout ciphertext + tag + nonce.
Client does not override fieldsDecrypted JSON must be an object and field names must be canonical.
Gateway can still see the titlePlain request fields are not E2EE-protected; put sensitive titles in ciphertext.
severity is not encryptedThis is recommended because the Gateway and platform push services need priority information.