# 2.5 Configuring and Running JSONAir

JSONAir is configured entirely through environment variables. There are no configuration files to edit. Variables can be provided in two ways:

* A `.env` file placed in the same directory as the binary
* System environment variables (set via `export`, a process manager, or a container runtime)

If a `.env` file is present, it is loaded automatically at startup. System environment variables take precedence if both are set.

***

## Environment Variables

### Required — Database

| Variable         | Description             | Example          |
| ---------------- | ----------------------- | ---------------- |
| `MYSQL_USERNAME` | Database username       | `jsonair`        |
| `MYSQL_PASSWORD` | Database password       | `s3cr3tpassword` |
| `MYSQL_HOST`     | Database hostname or IP | `127.0.0.1`      |
| `MYSQL_PORT`     | Database port           | `3306`           |
| `MYSQL_DATABASE` | Database name           | `jsonair`        |

### Optional — Database TLS

| Variable                | Description                                                                            | Default |
| ----------------------- | -------------------------------------------------------------------------------------- | ------- |
| `MYSQL_TLS`             | Set to `true` to enable TLS for the database connection                                | `false` |
| `MYSQL_TLS_SKIP_VERIFY` | Set to `true` to disable certificate verification. **Not recommended for production.** | `false` |

### Required — HTTP Server

| Variable      | Description                                                           | Example                   |
| ------------- | --------------------------------------------------------------------- | ------------------------- |
| `HTTP_LISTEN` | Address and port to listen on                                         | `:9191` or `0.0.0.0:9191` |
| `HTTP_MODE`   | Server mode. Must be one of: `production`, `release`, `debug`, `test` | `production`              |

> **Note:** `production` and `release` are equivalent — both run the Gin framework in release mode with HTTP access logging suppressed. Use `debug` during development for verbose request logging.

### Optional — HTTPS (TLS)

If `HTTP_TLS` is not set or is `false`, the server listens on plain HTTP.

| Variable    | Description                      | Example                                           |
| ----------- | -------------------------------- | ------------------------------------------------- |
| `HTTP_TLS`  | Set to `true` to enable HTTPS    | `true`                                            |
| `HTTP_CERT` | Path to the TLS certificate file | `/etc/letsencrypt/live/example.com/fullchain.pem` |
| `HTTP_KEY`  | Path to the TLS private key file | `/etc/letsencrypt/live/example.com/privkey.pem`   |

When `HTTP_TLS=true`, both `HTTP_CERT` and `HTTP_KEY` are required.

### Required — Security

| Variable                | Description                                                                                                                                             | Example                |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- |
| `JWT_TOKEN_SECRET`      | Secret used to sign and verify JWTs. Use a long, random string.                                                                                         | `openssl rand -hex 32` |
| `JWT_TOKEN_EXPIRE`      | JWT lifetime in **minutes**                                                                                                                             | `60`                   |
| `TOKEN_HMAC_SECRET`     | Secret used to HMAC-SHA256 hash PATs before storing or comparing them. Must match the value used when inserting keys into the database.                 | `openssl rand -hex 32` |
| `CONFIG_ENCRYPT_SECRET` | Secret used to derive the AES-256-GCM key for decrypting `config_data` at rest. Must match the secret used when encrypting data with `jsonair-encrypt`. | `openssl rand -hex 32` |

### Required — Process

| Variable | Description                                              | Example  |
| -------- | -------------------------------------------------------- | -------- |
| `RUNAS`  | Username to drop privileges to after binding to the port | `nobody` |

***

## Example `.env` File

Create a file named `.env` in the same directory as the `jsonair` binary. A minimal production setup looks like this:

```ini
# Database
MYSQL_USERNAME=jsonair
MYSQL_PASSWORD=your-strong-db-password
MYSQL_HOST=127.0.0.1
MYSQL_PORT=3306
MYSQL_DATABASE=jsonair
MYSQL_TLS=false

# HTTP
HTTP_LISTEN=:9191
HTTP_MODE=production
HTTP_TLS=false

# Security
JWT_TOKEN_SECRET=replace-with-output-of-openssl-rand-hex-32
JWT_TOKEN_EXPIRE=60
TOKEN_HMAC_SECRET=replace-with-a-different-openssl-rand-hex-32
CONFIG_ENCRYPT_SECRET=replace-with-a-third-openssl-rand-hex-32

# Process
RUNAS=nobody
```

For HTTPS, add:

```ini
HTTP_TLS=true
HTTP_CERT=/etc/letsencrypt/live/example.com/fullchain.pem
HTTP_KEY=/etc/letsencrypt/live/example.com/privkey.pem
```

> **Security:** Never commit a `.env` file containing real credentials to source control. Add `.env` to your `.gitignore`.

***

## Starting the Server

### Directly

If using a `.env` file, simply run the binary from the same directory:

```bash
cd /opt/jsonair
./jsonair
```

### Using export.sh

A convenience script is included at `cmd/jsonair/scripts/export.sh`. Copy and edit it with your values, then run it to set environment variables and start the server in one step:

```bash
cp cmd/jsonair/scripts/export.sh /opt/jsonair/start.sh
# Edit start.sh with your values
chmod +x /opt/jsonair/start.sh
./start.sh
```

### As a systemd Service

For production Linux deployments, run JSONAir as a systemd service. Create `/etc/systemd/system/jsonair.service`:

```ini
[Unit]
Description=JSONAir Configuration Server
After=network.target mysql.service
Wants=mysql.service

[Service]
Type=simple
User=root
WorkingDirectory=/opt/jsonair
EnvironmentFile=/etc/jsonair/jsonair.env
ExecStart=/opt/jsonair/jsonair
Restart=on-failure
RestartSec=5
KillMode=mixed
TimeoutStopSec=30

[Install]
WantedBy=multi-user.target
```

Place your environment variables in `/etc/jsonair/jsonair.env` (same format as a `.env` file), then enable and start the service:

```bash
sudo systemctl daemon-reload
sudo systemctl enable jsonair
sudo systemctl start jsonair
sudo systemctl status jsonair
```

> **Note:** The service starts as `root` so it can bind to the configured port, then immediately drops privileges to the user specified in `RUNAS`. This is the intended behavior.

***

## Verifying Startup

On a successful start you should see output similar to:

```
Banner :jsonair: -*> JSONAir! <*-
Banner :jsonair: Version: 1.0.0
Banner :jsonair: Champ Clark III & The Key9, Inc. Team [https://k9.io]
Banner :jsonair: Copyright (C) 2026 Key9, Inc. et al.
Info   :jsonair: Listening on ':9191' for traffic as UID: 65534.
```

The UID shown (`65534` on most Linux systems is `nobody`) confirms that privilege drop succeeded.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.k9.io/key9-identity/jsonair/2-install/2.5-configuration.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
