2.7 Encrypting Configuration Data

How to encrypt and decrypt configuration data for storage in JSONAir.

Configuration data stored in the JSONAir database may contain sensitive information — credentials, internal hostnames, API keys, and so on. JSONAir encrypts all config_data at rest using AES-256-GCM (authenticated encryption). The server decrypts transparently on every request — agents see no difference.

The jsonair-encrypt tool handles the encryption step before data is inserted into the database.


How It Works

Step
Who
What

1

Admin

Base64-encodes the raw configuration

2

Admin

Pipes the Base64 value through jsonair-encrypt

3

Admin

Inserts the encrypted output into config_data

4

Server

Decrypts config_data on every /config request

5

Agent

Receives Base64-encoded config — no change from its perspective

The encryption key is derived from CONFIG_ENCRYPT_SECRET using SHA-256, producing a 32-byte AES-256 key. Each encryption uses a randomly generated nonce (IV), so two encryptions of the same data produce different ciphertext. The GCM authentication tag ensures any tampering with the stored value is detected and rejected by the server.


The jsonair-encrypt Tool

jsonair-encrypt reads from stdin and writes to stdout. It requires CONFIG_ENCRYPT_SECRET to be set — either as an environment variable or in a .env file in the same directory.

Flag
Behaviour

(none)

Encrypt: reads plaintext, writes encrypted Base64

-d

Decrypt: reads encrypted Base64, writes plaintext

Building

Encrypting

Decrypting

Encrypting a file

Or as a single pipeline:

Using a .env file

If you place a .env file in the same directory as jsonair-encrypt, it will be loaded automatically:

Then simply:


Full Workflow — Adding a New Configuration

1. Base64-encode your configuration

2. Encrypt the output

Copy the output — it is the value for config_data.

3. Insert into the database


Key Management

CONFIG_ENCRYPT_SECRET must be the same value on both the jsonair-encrypt tool (used when inserting data) and the jsonair server (used when serving data). If they differ, the server will fail to decrypt and return a 500 error.

Best practices:

  • Generate a strong secret: openssl rand -hex 32

  • Keep CONFIG_ENCRYPT_SECRET separate from TOKEN_HMAC_SECRET and JWT_TOKEN_SECRET — use a distinct value for each

  • Store the secret in a secrets manager (Vault, AWS Secrets Manager, etc.) in production rather than in a .env file

  • Treat key rotation as a migration (see below)


Migrating Existing (Unencrypted) Data

If you have existing config_data rows that were stored as plain Base64 (before encryption was introduced), you must re-encrypt them before deploying the new server version. The server expects all config_data to be encrypted — it will return a 500 on any row that is not.

Migration steps

  1. Set CONFIG_ENCRYPT_SECRET in your environment.

  2. For each existing row, retrieve the current config_data value and pipe it through jsonair-encrypt:

  1. Repeat for every row, then deploy the new server.


Key Rotation

To rotate CONFIG_ENCRYPT_SECRET:

  1. Decrypt every config_data row using the old key (pass -d).

  2. Re-encrypt each row using the new key.

  3. Update CONFIG_ENCRYPT_SECRET on the server and restart.

Until step 3 is complete, keep the old server running — it cannot read rows encrypted with the new key.

Last updated