# 2.6 Configuring and Running the JSONAir Agent

The JSONAir agent (`jsonair-agent`) is a lightweight process that runs on the target host. It periodically polls the JSONAir server for configuration updates, writes the result to a local file, and executes a reload command when the configuration changes.

Like the server, the agent is configured entirely through environment variables or a `.env` file placed in the same directory as the binary.

***

## How It Works

1. The agent authenticates with the JSONAir server using its PAT and receives a short-lived JWT.
2. On each polling interval, it fetches the configuration for the configured `type` and `name`.
3. It computes a SHA-256 hash of the retrieved data and compares it to the hash of the file currently on disk.
4. If the hashes match, nothing has changed — the agent sleeps and tries again next interval.
5. If the hashes differ, the agent backs up the existing file, writes the new configuration to disk, and executes the `RELOAD_COMMAND`.

***

## Resilience — Surviving a Server Outage

The agent is designed to keep running even if the JSONAir server is temporarily unreachable. It will **never exit** due to a network error or a bad HTTP response from the server. Instead, it logs the error, sleeps for the configured `SLEEP` interval, and retries on the next cycle.

| Condition                                           | Agent Behaviour                                     |
| --------------------------------------------------- | --------------------------------------------------- |
| Server unreachable (connection refused, timeout)    | Logs the error, sleeps, retries                     |
| Server returns an unexpected HTTP status (e.g. 500) | Logs the status, sleeps, retries                    |
| Server returns 401 (JWT expired)                    | Re-authenticates using the PAT, retries immediately |
| Server returns 200                                  | Processes the configuration normally                |

This means the last successfully written configuration file remains in place on disk and the consuming application continues running uninterrupted while the JSONAir server is down.

***

## Environment Variables

### Required — JSONAir Server

| Variable       | Description                                                           | Example                       |
| -------------- | --------------------------------------------------------------------- | ----------------------------- |
| `JSONAIR_PAT`  | Plain-text Personal Access Token used to authenticate with the server | `your-secret-pat`             |
| `JSONAIR_URL`  | Base URL of the JSONAir server (no trailing slash)                    | `https://jsonair.example.com` |
| `JSONAIR_TYPE` | Configuration type to retrieve                                        | `suricata`                    |
| `JSONAIR_NAME` | Configuration name to retrieve                                        | `suricata.yaml`               |

### Required — Local File Management

| Variable         | Description                                                                                                                            | Example                        |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ |
| `CONFIG_FILE`    | Full path where the configuration file will be written                                                                                 | `/etc/suricata/suricata.yaml`  |
| `RELOAD_COMMAND` | Command to execute after a configuration update is written to disk. Runs with the arguments split on whitespace — no shell is invoked. | `/usr/bin/killall -1 suricata` |
| `PRUNE`          | Number of backup files to keep. Older backups are deleted automatically.                                                               | `5`                            |
| `SLEEP`          | Polling interval in seconds                                                                                                            | `300`                          |

### Optional — File Permissions

| Variable | Description                                                   | Default |
| -------- | ------------------------------------------------------------- | ------- |
| `MASK`   | Octal file permission mask for the written configuration file | `0600`  |

### Required — Process

| Variable | Description                                  | Example  |
| -------- | -------------------------------------------- | -------- |
| `RUNAS`  | Username to drop privileges to after startup | `nobody` |

***

## The RELOAD\_COMMAND

The `RELOAD_COMMAND` is executed every time the agent detects a configuration change and writes a new file to disk. This allows the agent to signal whatever service consumes the configuration to reload it — without requiring a restart.

The command is split on whitespace and executed directly (no shell). This prevents shell injection. Use full paths to binaries.

**Common examples:**

| Use case                 | RELOAD\_COMMAND                   |
| ------------------------ | --------------------------------- |
| Send SIGHUP to a process | `/usr/bin/killall -1 suricata`    |
| Reload via systemctl     | `/usr/bin/systemctl reload nginx` |
| Run a custom script      | `/usr/local/bin/reload-app.sh`    |

> **Note:** The reload command runs as the user specified by `RUNAS`. Make sure that user has permission to execute the command.

***

## Example `.env` File

```ini
# JSONAir Server
JSONAIR_PAT=your-secret-pat
JSONAIR_URL=https://jsonair.example.com
JSONAIR_TYPE=suricata
JSONAIR_NAME=suricata.yaml

# Local file management
CONFIG_FILE=/etc/suricata/suricata.yaml
RELOAD_COMMAND=/usr/bin/killall -1 suricata
PRUNE=5
SLEEP=300
MASK=0644

# Process
RUNAS=nobody
```

***

## Starting the Agent

### Directly

```bash
cd /opt/jsonair-agent
./jsonair-agent
```

### Using export.sh

A convenience script is included at `cmd/jsonair-agent/scripts/export.sh`. Copy and edit it with your values:

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

### As a systemd Service

Create `/etc/systemd/system/jsonair-agent.service`:

```ini
[Unit]
Description=JSONAir Agent
After=network.target

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

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

Place your environment variables in `/etc/jsonair/agent.env`, then enable and start the service:

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

***

## Backup Behavior

Each time a configuration change is detected, the agent creates a timestamped backup of the existing file before overwriting it:

```
/etc/suricata/suricata.yaml.20260422-143022-123456789.bak
```

The `PRUNE` variable controls how many backups are retained. Once the limit is exceeded, the oldest backup is automatically deleted.


---

# 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.6-agent-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.
