# Configuration

LLMS index: [llms.txt](/llms.txt)

---

## Configuration

psLens reads a `config.yaml` file from the same directory as the binary. There is no database to set up; persistent data (report results, alert state) is stored in an embedded NATS data store.

### Full Configuration Examples

psLens supports configuration in either YAML (`config.yaml`) or JSON (`config.json`) format. The application detects the format automatically on boot.

#### YAML Configuration Example (`config.yaml`)

```yaml
# Schema validation pointer (optional, for IDE support)
# yaml-language-server: $schema=http://localhost:8080/static/config-schema.json

server:
  port: 8080
  host: "0.0.0.0"
  natsStoreDir: "./data/nats"
  projectStoreDir: "./data/projects"
  dmsStoreDir: "./data/dms"
  appBaseURL: "http://localhost:8080"
  recentlyViewed:
    maxItems: 20

databases:
  - name: "PROD"
    description: "Production PeopleSoft HR"
    baseURL: "https://psft.example.com:8000/PSIGW/RESTListeningConnector/PSFT_HR/CHG_SWS_PSOFTQL/"
    username: "PSLENS_API"
    password: "your-api-password"
    piaURL: "https://psft.example.com/psp/ps/"
    timezone: "America/Chicago"
    production: true
    alerts:
      enabled: true
      intervalMinutes: 10

  - name: "DEV"
    description: "Development Environment"
    baseURL: "https://psftdev.example.com:8000/PSIGW/RESTListeningConnector/PSFT_HR/CHG_SWS_PSOFTQL/"
    username: "PSLENS_API"
    password: "dev-api-password"
    timezone: "America/Chicago"
    production: false

alerts:
  enabled: true
  intervalMinutes: 5
  checks:
    long_running_processes:
      enabled: true
      thresholdMinutes: 20
    process_errors:
      enabled: true
      lookbackHours: 24
    ib_operation_errors:
      enabled: true
      lookbackHours: 24
    ib_pub_contract_errors:
      enabled: true
      lookbackHours: 24
    ib_sub_contract_errors:
      enabled: true
      lookbackHours: 24
    ib_operation_stalled:
      enabled: true
      thresholdMinutes: 30
    ib_pub_contract_stalled:
      enabled: true
      thresholdMinutes: 30
    ib_sub_contract_stalled:
      enabled: true
      thresholdMinutes: 30
  genericSWSAlerts:
    - id: "stale_users"
      name: "Stale User Accounts"
      enabled: true
      severity: "warning"
      alertOn: "row_found"
      message: "Warning: Stale user accounts detected"
      query:
        records:
          - recordName: "PSOPRDEFN"
            sqlWhereClause: "LASTUPDDTTM < CAST('2026-01-01' AS TIMESTAMP) AND ACCTLOCK = 0"
        rowLimit: 5

auth:
  enabled: true
  authorizedUsers:
    - "admin@example.com"
    - "auditor@example.com"

smtp:
  host: "smtp.mailtrap.io"
  port: "2525"
  username: "smtp-user"
  password: "smtp-password"
  fromName: "psLens Alerts"
  fromEmail: "alerts@pslens.example.com"

notifications:
  subscriptions:
    - id: "team-email"
      enabled: true
      alertTypes: ["*"]
      databases: ["PROD"]
      severityMin: "warning"
      type: "email"
      target: "psoft-alerts@example.com"
    - id: "slack-webhook"
      enabled: true
      alertTypes: ["process_errors", "ib_operation_errors"]
      databases: ["*"]
      type: "webhook"
      target: "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"
    - id: "teams-webhook"
      enabled: true
      alertTypes: ["*"]
      databases: ["*"]
      type: "webhook"
      target: "https://example.webhook.office.com/webhookb2/..."
```

#### JSON Configuration Example (`config.json`)

```json
{
  "$schema": "http://localhost:8080/static/config-schema.json",
  "server": {
    "port": 8080,
    "host": "0.0.0.0",
    "natsStoreDir": "./data/nats",
    "projectStoreDir": "./data/projects",
    "dmsStoreDir": "./data/dms",
    "appBaseURL": "http://localhost:8080",
    "recentlyViewed": {
      "maxItems": 20
    }
  },
  "databases": [
    {
      "name": "PROD",
      "description": "Production PeopleSoft HR",
      "baseURL": "https://psft.example.com:8000/PSIGW/RESTListeningConnector/PSFT_HR/CHG_SWS_PSOFTQL/",
      "username": "PSLENS_API",
      "password": "your-api-password",
      "piaURL": "https://psft.example.com/psp/ps/",
      "timezone": "America/Chicago",
      "production": true,
      "alerts": {
        "enabled": true,
        "intervalMinutes": 10
      }
    },
    {
      "name": "DEV",
      "description": "Development Environment",
      "baseURL": "https://psftdev.example.com:8000/PSIGW/RESTListeningConnector/PSFT_HR/CHG_SWS_PSOFTQL/",
      "username": "PSLENS_API",
      "password": "dev-api-password",
      "timezone": "America/Chicago",
      "production": false
    }
  ],
  "alerts": {
    "enabled": true,
    "intervalMinutes": 5,
    "checks": {
      "long_running_processes": {
        "enabled": true,
        "thresholdMinutes": 20
      },
      "process_errors": {
        "enabled": true,
        "lookbackHours": 24
      },
      "ib_operation_errors": {
        "enabled": true,
        "lookbackHours": 24
      },
      "ib_pub_contract_errors": {
        "enabled": true,
        "lookbackHours": 24
      },
      "ib_sub_contract_errors": {
        "enabled": true,
        "lookbackHours": 24
      },
      "ib_operation_stalled": {
        "enabled": true,
        "thresholdMinutes": 30
      },
      "ib_pub_contract_stalled": {
        "enabled": true,
        "thresholdMinutes": 30
      },
      "ib_sub_contract_stalled": {
        "enabled": true,
        "thresholdMinutes": 30
      }
    },
    "genericSWSAlerts": [
      {
        "id": "stale_users",
        "name": "Stale User Accounts",
        "enabled": true,
        "severity": "warning",
        "alertOn": "row_found",
        "message": "Warning: Stale user accounts detected",
        "query": {
          "records": [
            {
              "recordName": "PSOPRDEFN",
              "sqlWhereClause": "LASTUPDDTTM < CAST('2026-01-01' AS TIMESTAMP) and ACCTLOCK = 0"
            }
          ],
          "rowLimit": 5
        }
      }
    ]
  },
  "auth": {
    "enabled": true,
    "authorizedUsers": [
      "admin@example.com",
      "auditor@example.com"
    ]
  },
  "smtp": {
    "host": "smtp.mailtrap.io",
    "port": "2525",
    "username": "smtp-user",
    "password": "smtp-password",
    "fromName": "psLens Alerts",
    "fromEmail": "alerts@pslens.example.com"
  },
  "notifications": {
    "subscriptions": [
      {
        "id": "team-email",
        "enabled": true,
        "alertTypes": ["*"],
        "databases": ["PROD"],
        "severityMin": "warning",
        "type": "email",
        "target": "psoft-alerts@example.com"
      },
      {
        "id": "slack-webhook",
        "enabled": true,
        "alertTypes": ["process_errors", "ib_operation_errors"],
        "databases": ["*"],
        "type": "webhook",
        "target": "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"
      },
      {
        "id": "teams-webhook",
        "enabled": true,
        "alertTypes": ["*"],
        "databases": ["*"],
        "type": "webhook",
        "target": "https://example.webhook.office.com/webhookb2/..."
      }
    ]
  }
}
```

---

---

### Server Settings

The `server` section controls how psLens listens for incoming connections and where it stores data.

|    Setting     |    Default    |                             Description                              |
| -------------- | ------------- | -------------------------------------------------------------------- |
| `port`         | `8080`        | TCP port psLens listens on                                           |
| `host`         | `0.0.0.0`     | Network interface to bind (use `127.0.0.1` to restrict to localhost) |
| `natsStoreDir` | `./data/nats` | Directory for persistent NATS data (report results, alert history)   |

> **Tip:** The `natsStoreDir` should be on a persistent volume. If psLens restarts, report results and alert history stored here are preserved.

---

### Database Connections

You can configure one or more PeopleSoft databases under the `databases` list. psLens monitors the health of each connection and shows status on the dashboard.

|    Setting    | Required |                                                                 Description                                                                 |
| ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`        | Yes      | Short identifier shown in the UI (e.g., `PROD`, `DEV`)                                                                                      |
| `description` | Yes      | Human-readable label for the database                                                                                                       |
| `baseURL`     | Yes      | Full URL to the SWS psoftQL endpoint, including the service name                                                                            |
| `username`    | Yes      | PeopleSoft operator ID for API authentication                                                                                               |
| `password`    | Yes      | Password for the operator ID                                                                                                                |
| `piaURL`      | No       | Base URL for PeopleSoft Internet Architecture (used for deep links to PeopleSoft pages, if supported)                                       |
| `timezone`    | No       | IANA timezone name for the database server (e.g., `America/Chicago`). Defaults to `UTC` if not set. Used to interpret timestamps correctly. |

#### The baseURL Format

The `baseURL` is the Integration Broker REST endpoint for the SWS service. It follows this pattern:

```text
https://{igw-host}:{port}/PSIGW/RESTListeningConnector/{database-name}/CHG_SWS_PSOFTQL/
```

The trailing slash is required.

#### Environment Variable Overrides

Sensitive settings like passwords can be overridden with environment variables to avoid storing them in `config.yaml`. The override format is:

```text
PSLENS_DB_{NAME}_{FIELD}
```

Where `{NAME}` matches the `name` field of the database in `config.yaml` (case-sensitive).

For example, to override the password for a database named `PROD`:

```bash
export PSLENS_DB_PROD_PASSWORD="my-secure-password"
```

---

### Alerts Configuration

The `alerts` section controls the background alert checking system. See the [Alerts](/docs/alerts/) section for details on what each alert detects.

#### Top-Level Alert Settings

|      Setting       | Default |                                                    Description                                                    |
| ------------------ | ------- | ----------------------------------------------------------------------------------------------------------------- |
| `enabled`          | `false` | Whether to run background alert checks                                                                            |
| `intervalMinutes`  | `5`     | How often (in minutes) to run all alert checks                                                                    |
| `genericSWSAlerts` | `[]`    | List of queryable SWS alerts. See [Generic SWS Alerts](/docs/alerts/generic-alerts/) for details. |

#### Alert Check Settings

Each alert type under `checks` supports some or all of the following settings:

|       Setting       |                                         Description                                         |
| ------------------- | ------------------------------------------------------------------------------------------- |
| `enabled`           | Whether this check is active                                                                |
| `thresholdMinutes`  | For stalled/long-running checks: how many minutes before flagging (default varies by check) |
| `lookbackHours`     | For error checks: how many hours back to look for failures (default varies by check)        |
| `excludeProcesses`  | List of process names to skip (for process-related checks)                                  |
| `excludeOperations` | List of IB operation names to skip (for Integration Broker checks)                          |

#### Available Alert Checks

|         Check Key         |               Name                |                                                               Description                                                               |
| ------------------------- | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `long_running_processes`  | Long-Running Processes            | Flags processes running longer than `thresholdMinutes` (default: 20 min)                                                                |
| `process_errors`          | Process Errors                    | Finds processes that failed within `lookbackHours` (default: 24 hours)                                                                  |
| `ib_operation_errors`     | IB Operation Errors               | Finds async IB operations in Error or Timeout status within `lookbackHours` (default: 24 hours)                                         |
| `ib_pub_contract_errors`  | IB Publication Contract Errors    | Finds pub contracts in Error or Timeout status within `lookbackHours` (default: 24 hours)                                               |
| `ib_sub_contract_errors`  | IB Subscription Contract Errors   | Finds sub contracts in Error or Timeout status within `lookbackHours` (default: 24 hours)                                               |
| `ib_operation_stalled`    | IB Operations Stalled             | Finds async IB operations stuck in New or Working status longer than `thresholdMinutes` (default: 30 min)                               |
| `ib_pub_contract_stalled` | IB Publication Contracts Stalled  | Finds pub contracts stuck in New or Working status longer than `thresholdMinutes` (default: 30 min)                                     |
| `ib_sub_contract_stalled` | IB Subscription Contracts Stalled | Finds sub contracts stuck in New or Working status longer than `thresholdMinutes` (default: 30 min)                                     |
| `locked_oprid_processes`  | Locked OPRID Scheduled Processes  | Finds queued or scheduled processes whose submitting OPRID has a locked account                                                         |
| `backlogged_processes`    | Backlogged Processes              | Detects queued/blocked processes whose scheduled run time is more than `thresholdMinutes` in the past (default: 30 min)                 |
| `failed_logins`           | Failed Logins                     | Detects users with excessive failed login attempts in `PSPTLOGINAUDIT` (defaults to > `thresholdCount` of 5)                            |
| `process_run_check`       | Process Run Check                 | Monitors configured critical processes and alerts when they haven't run successfully within their configured time window                |
| `ib_operation_volume`     | Abnormal IB Operation Volume      | Detects when IB operation instance volume exceeds the historical average by a percentage specified in `thresholdCount` (default: 50)    |
| `ib_pub_contract_volume`  | Abnormal IB Pub Contract Volume   | Detects when IB publication contract volume exceeds the historical average by a percentage specified in `thresholdCount` (default: 50)  |
| `ib_sub_contract_volume`  | Abnormal IB Sub Contract Volume   | Detects when IB subscription contract volume exceeds the historical average by a percentage specified in `thresholdCount` (default: 50) |
| `ib_sync_exceptions`      | IB Sync Operation Exceptions      | Detects synchronous service operations with errors in `PSIBLOGHDR` within `lookbackHours` (default: 24 hours)                           |
| `no_process_completed`    | No Process Completed              | Fires when no process has successfully completed within the `lookbackHours` (default: 1 hour)                                           |
| `ib_down`                 | Integration Broker Down           | Alerts when SWS REST endpoint connection failures indicate the Integration Broker is down                                               |
| `weblib_down`             | Web Server / WebLib Down          | Alerts when PeopleSoft Web Server is down or configured WebLib URLs fail to respond                                                     |
| `ib_no_active_domain`     | IB No Active Domain               | Alerts when there is no active domain found in `PSAPMSGDOMSTAT`                                                                         |
| `ib_dispatcher_down`      | IB Dispatcher Down                | Alerts when an Integration Broker dispatcher process is inactive or has not updated status within `thresholdMinutes` (default: 10 min)  |
| `ib_nodes_down`           | IB Nodes Down                     | Alerts when there are entries in `PSNODESDOWN` indicating message nodes are down                                                        |

---

### Authentication Settings

The `auth` section configures native email-based magic link authentication. When enabled, users must log in using an email verification code sent to an address on the authorized allowlist.

|      Setting      | Default |                           Description                            |
| ----------------- | ------- | ---------------------------------------------------------------- |
| `enabled`         | `false` | Set to `true` to require magic link authentication for all pages |
| `authorizedUsers` | `[]`    | List of email addresses allowed to log in (case-insensitive)     |

---

### SMTP Settings

The `smtp` section holds global SMTP credentials. It is a root-level block used both for sending magic link authentication emails and dispatching email notifications.

|   Setting   | Default  |                                Description                                |
| ----------- | -------- | ------------------------------------------------------------------------- |
| `host`      | -        | SMTP server hostname/IP                                                   |
| `port`      | -        | SMTP port (e.g. `25`, `465`, `587`, `2525`)                               |
| `username`  | -        | Username for SMTP authentication                                          |
| `password`  | -        | Password for SMTP authentication (encrypted at rest if master key is set) |
| `fromName`  | `psLens` | Sender name shown in emails                                               |
| `fromEmail` | -        | Sender email address for SMTP                                             |

---

### Notifications & Webhooks Settings

The `notifications` section defines where alert messages are dispatched. You can configure individual subscriptions for email or webhook endpoints.

#### Subscription Sub-settings

Under `notifications.subscriptions`, define a list of targets:

|   Property    |      Type       |                                          Description                                          |
| ------------- | --------------- | --------------------------------------------------------------------------------------------- |
| `id`          | String          | Unique identifier for the subscription                                                        |
| `enabled`     | Boolean         | Activates or silences the subscription                                                        |
| `alertTypes`  | List of Strings | Alert check keys to match (e.g. `["*"]` for all, or `["process_errors"]`)                     |
| `databases`   | List of Strings | Database names to match (e.g. `["*"]` or `["PROD"]`)                                          |
| `severityMin` | String          | Minimum alert severity (`"info"`, `"warning"`, `"critical"`)                                  |
| `type`        | String          | Dispatch protocol: `"email"` or `"webhook"`                                                   |
| `target`      | String          | Target destination: email address, Slack Webhook URL, or Microsoft Teams Incoming Webhook URL |

*Note: psLens detects Slack and MS Teams webhook URLs from the URL pattern and formats the payload for each.*

---

### JSON Schema Validation

To enable autocompletion, tooltips, and real-time schema validation within your IDE (such as VS Code), use the built-in JSON schema:

#### Option A: Live URL (Online)

If your psLens server is running locally (e.g., on port 8080), you can add the `$schema` parameter to the top of your JSON config file:

```json
{
  "$schema": "http://localhost:8080/static/config-schema.json",
  "server": {
    "port": 8080
  }
}
```

#### Option B: Local File Reference (Offline)

To configure your workspace offline, reference the schema file directly. In VS Code, add this to your `.vscode/settings.json`:

```json
{
  "json.schemas": [
    {
      "fileMatch": ["config.json"],
      "url": "./static/config-schema.json"
    }
  ],
  "yaml.schemas": {
    "./static/config-schema.json": ["config.yaml"]
  }
}
```

---

### Securing psLens

For production deployments, you should restrict access to the psLens interface.

#### Recommended Security Controls

1. **Enable Built-In Magic Link Auth**:
   Turn on `auth.enabled` and configure SMTP credentials and `authorizedUsers` to require code validation on login.
2. **Setup a Master Key (`PSLENS_MASTER_KEY`)**:
   Provide a 32-byte (64 hex characters) key in the `PSLENS_MASTER_KEY` environment variable. All database and SMTP passwords entered in the UI or configuration are then encrypted at rest using AES-256-GCM.
3. **Reverse Proxy / VPN**:
   Place psLens behind a reverse proxy (e.g., Cloudflare Access, oauth2-proxy, nginx, Tailscale) to delegate authentication to your company's Identity Provider (SAML/OIDC). When using an external SSO proxy, you can keep `auth.enabled` disabled and restrict the psLens binary to bind only on `127.0.0.1` or internal networks.

> **Warning:** Never expose psLens to the public internet without either turning on the built-in magic-link auth or placing an authenticated reverse proxy in front of it. Doing so exposes read access to PeopleSoft system metadata.
