Configuration
Categories:
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)
# 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
downtimes:
- name: "Nightly Downtime"
enabled: true
startTime: "22:00"
endTime: "06:00"
daysOfWeek: [1, 2, 3, 4, 5]
- name: "Weekend Maintenance"
enabled: true
startAt: "2026-07-04T00:00:00Z"
endAt: "2026-07-05T23:59:59Z"
alerts:
enabled: true
intervalMinutes: 5
checks:
long_running_processes:
enabled: true
thresholdMinutes: 20
anomalyMultiplier: 4.0
anomalyMinBaselineMinutes: 10
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/..."
reports:
- id: "security-full-access-permlists"
enabled: true
dbNames: ["PROD"]
schedule:
interval: "daily"
timeOfDay: "02:00"
emailTarget: "psoft-alerts@example.com"
JSON Configuration Example (config.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,
"anomalyMultiplier": 4.0,
"anomalyMinBaselineMinutes": 10
},
"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/..."
}
]
},
"reports": [
{
"id": "security-full-access-permlists",
"enabled": true,
"dbNames": ["PROD"],
"schedule": {
"interval": "daily",
"timeOfDay": "02:00"
},
"emailTarget": "psoft-alerts@example.com"
}
]
}
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
natsStoreDirshould 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. |
downtimes | No | List of scheduled downtime windows to suppress alerts and monitoring. |
notifications | No | Database-specific notification delivery settings (bypasses global subscriptions). |
Scheduled Downtimes
Downtimes allow silencing alerts and pausing connection health checks for non-production environments that go offline regularly (e.g., overnight or on weekends).
Each entry in the downtimes list supports the following settings:
| Setting | Required | Description |
|---|---|---|
name | Yes | Identifier for the downtime window. |
enabled | Yes | Whether the downtime rule is active. |
startTime | No | Start time of day in HH:MM format (e.g., 22:00). Set together with endTime. |
endTime | No | End time of day in HH:MM format (e.g., 06:00). An end time earlier than the start crosses midnight. 24:00 means end of day, so an all-day window is 00:00–24:00. |
daysOfWeek | No | Array of integers for days of week. 0 is Sunday, 1 is Monday, …, 6 is Saturday. Omit to apply every day. An overnight window belongs to the day it starts. |
startAt | No | Specific start timestamp in RFC3339 format (e.g., 2026-07-04T00:00:00Z) for one-off maintenance. |
endAt | No | Specific end timestamp in RFC3339 format (e.g., 2026-07-05T23:59:59Z) for one-off maintenance. |
startTime/endTime are wall-clock times in the connection’s timezone (UTC if unset). Rules are validated when the configuration is saved; a rule must define a recurring window (startTime + endTime) and/or a one-off range (startAt/endAt).
When a database is in a scheduled downtime, the connection manager sets its status to Downtime on the dashboard, background alert checks are skipped (including the Integration Broker down check), email/webhook notifications are suppressed, and the database does not count against overall health. When the window ends, psLens rechecks the connection immediately and treats the first failed check as a real outage — size windows to cover the system’s boot time.
A weekend window that spans from Saturday evening to Monday morning is composed of two rules, since an overnight window belongs to its start day:
downtimes:
- name: "Weekend nights"
enabled: true
startTime: "22:00"
endTime: "06:00"
daysOfWeek: [6, 0] # Sat night -> Sun morning, Sun night -> Mon morning
- name: "Sunday all day"
enabled: true
startTime: "00:00"
endTime: "24:00"
daysOfWeek: [0]
The baseURL Format
The baseURL is the Integration Broker REST endpoint for the SWS service. It follows this pattern:
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:
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:
export PSLENS_DB_PROD_PASSWORD="my-secure-password"
Database-Specific Notifications
By default, alert notifications for all databases are routed using global subscription rules (see Notifications & Webhooks Settings).
However, you can configure database-specific notifications under the notifications property of a database entry. Doing so acts as a complete override: all alerts (regardless of severity level) and resolutions for that specific database are routed directly to the database-level targets, bypassing global subscriptions entirely.
Each entry in the notifications block supports the following settings:
| Setting | Type | Description |
|---|---|---|
emailEnabled | Boolean | Activates email notifications for this database. |
emailTarget | String | Comma-separated list of recipient email addresses. |
webhookEnabled | Boolean | Activates webhook notifications for this database. |
webhookTarget | String | Webhook destination URL (e.g., Slack, MS Teams, or a generic endpoint). |
Example database connection configuration with inline notifications:
databases:
- name: "PROD"
description: "Production Environment"
baseURL: "https://psft.example.com:8000/PSIGW/RESTListeningConnector/PSFT_HR/CHG_SWS_PSOFTQL/"
username: "PSLENS_API"
password: "securepassword"
notifications:
emailEnabled: true
emailTarget: "prod-alerts@example.com"
webhookEnabled: true
webhookTarget: "https://hooks.slack.com/services/..."
Alerts Configuration
The alerts section controls the background alert checking system. See the 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 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). For long_running_processes, this is the fallback static threshold. |
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) |
anomalyMultiplier | Multiplier applied to the rolling median duration to calculate the dynamic threshold (default: 4.0) |
anomalyMinBaselineMinutes | Minimum baseline duration in minutes. Dynamic thresholds are capped to be at least this value to prevent false alerts on very fast processes (default: 10) |
Available Alert Checks
| Check Key | Name | Description |
|---|---|---|
long_running_processes | Long-Running Processes | Flags processes running longer than their rolling median-based expected runtime (or thresholdMinutes fallback) |
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 processes currently stuck in a Queued or Blocked state longer than thresholdMinutes (default: 30 min) |
queue_latency | Queue Latency | Detects processes that experienced a start delay (BEGINDTTM - RUNDTTM) greater than thresholdMinutes (default: 15 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). Disabled by default. |
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 email server is edited in the app under Settings → Notifications (Email Server card). Changes apply to alert and report emails immediately; login (magic link) emails pick them up after the next server restart. A blank password on save keeps the stored password.
The smtp block in config.yaml seeds the email server on first boot only; after that, the settings saved in the UI are authoritative. PSLENS_SMTP_* environment variables override both and make the card read-only in the UI.
| 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 |
Outbound email can be tested on the same card: enter a recipient address under Send Test Email and click Send Test. The result is recorded in Alert Delivery History.
Notifications & Webhooks Settings
Notification rules decide where alert messages are dispatched. They are managed in the app under Settings → Notifications (Notification Rules card), where each rule can be added, edited, tested, and deleted. The notifications section in config.yaml defines the same rules in file form (the file calls them subscriptions).
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.
Reports Scheduling Settings
The reports section allows you to define static scheduling configs and completion notifications directly in the configuration file.
Report Properties
Under reports, define a list of scheduled report objects:
| Property | Type | Description |
|---|---|---|
id | String | The unique report definition ID (e.g. security-full-access-permlists) |
enabled | Boolean | Enables or disables the scheduled execution of this report (defaults to true) |
dbNames | List of Strings | Database names to target (e.g., ["PROD"]). If omitted, targets all active databases. |
schedule | Object | Timing recurrence properties (see below) |
emailTarget | String | Optional email address to send report completion notifications to |
webhookTarget | String | Optional Slack, MS Teams, or JSON webhook endpoint to send notifications to |
Schedule Recurrence Properties
Under reports.schedule, define when the job should run:
| Property | Type | Description |
|---|---|---|
interval | String | Recurrence frequency: "daily", "weekly", or "monthly" |
timeOfDay | String | 24-hour format execution time ("HH:MM", e.g., "02:00") in server local time |
dayOfWeek | Integer | Optional. Day of the week for weekly runs (0 for Sunday, 1 for Monday, …, 6 for Saturday) |
dayOfMonth | Integer | Optional. Day of the month for monthly runs (1 to 28) |
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:
{
"$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.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
- Enable Built-In Magic Link Auth:
Turn on
auth.enabledand configure SMTP credentials andauthorizedUsersto require code validation on login. - Setup a Master Key (
PSLENS_MASTER_KEY): Provide a 32-byte (64 hex characters) key in thePSLENS_MASTER_KEYenvironment variable. All database and SMTP passwords entered in the UI or configuration are then encrypted at rest using AES-256-GCM. - 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.enableddisabled and restrict the psLens binary to bind only on127.0.0.1or 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.
Active Configuration and Hot-Reloading
psLens keeps its settings in two places:
- Active configuration (embedded key-value store): when you edit connections, credentials, email settings, alert schedules, or notification rules in the web interface, changes are saved here and take effect immediately without a server restart. Every change is kept as a numbered revision under Settings → Change History, where any revision can be viewed or restored.
config.yamlon disk: the bootstrap file that seeds the active configuration on first boot. Saved changes are also written back to it as a backup on the persistent storage volume.
Configuration Seeding
On initial boot, or when the environment variable PSLENS_FORCE_SEED_CONFIG=true is set, the application seeds the active configuration from config.yaml.
If the active configuration drifts from config.yaml, the Settings → Advanced page shows a “Settings differ from the config.yaml file on disk” notice. To resolve it:
- Click Load settings from config.yaml on the Advanced page to overwrite the active configuration with the file contents (this discards changes made in the UI).
- If the file path is writable, click Overwrite config.yaml on disk to write the active configuration to the file. On containerized or ephemeral deployment models (like fly.io without persistent storage volumes), these local file changes will be lost when the container restarts or is re-deployed.
- Alternatively, copy the Active Configuration YAML from the Advanced page into your
config.yamlfile to bring the disk file in sync.
Secret Key Management (Encryption at Rest)
When the PSLENS_MASTER_KEY environment variable is set with a 32-byte hex-encoded key, all database and SMTP passwords entered in the UI are encrypted at rest. If the master key is not configured, passwords are saved in plaintext.
Rotating the Master Key
To rotate the cryptographic key used for credential encryption:
- Enter a new 32-byte hex key in the Rotate Encryption Key field.
- Click Rotate Master Key. The server decrypts all stored credentials using the old key and re-encrypts them with the new key in NATS KV.
- Update the
PSLENS_MASTER_KEYenvironment variable in your deployment configuration (e.g., Fly.io secrets or.envfile) to match the new key before restarting the container. If the container restarts with a mismatched key, it cannot decrypt the configuration.