Configuration

psLens reads a config.yaml file from the same directory as the binary.

Configuration

psLens reads a config.yaml file from the same directory as the binary. There is no external database server to set up. An embedded NATS data store saves persistent state (configuration history, report results, and alert state).

Configuration Synchronization

psLens synchronizes active configuration automatically between config.yaml and the embedded NATS KV store:

  • Bare-Minimum Boot: The server can boot with zero configured database connections. Administrators can add database connections later directly through the Settings UI at /settings.
  • Disk Edit Recognition: On boot, psLens checks modification timestamps. If config.yaml is newer than the NATS KV snapshot, psLens syncs file changes into NATS KV automatically.
  • UI Auto Write-Back: Changes made via the Settings UI update NATS KV and automatically write back to config.yaml on disk.

Password & Secret Security Options

psLens provides three secure methods for passing database and server credentials:

  1. 1Password Secret References (op://...): Pass 1Password Secret URIs directly in config.yaml or JSON configurations:

    databases:
      - name: "PROD"
        username: "PSLENS_API"
        password: "op://Employee-Vault/PeopleSoft-Prod-API/password"
    
    smtp:
      host: "op://Employee-Vault/SMTP-Mailtrap/host"
      port: "op://Employee-Vault/SMTP-Mailtrap/port"
      username: "op://Employee-Vault/SMTP-Mailtrap/username"
      password: "op://Employee-Vault/SMTP-Mailtrap/password"
      fromName: "op://Employee-Vault/SMTP-Mailtrap/from_name"
      fromEmail: "op://Employee-Vault/SMTP-Mailtrap/from_email"
    
    auth:
      oidc:
        issuerUrl: "op://Employee-Vault/OIDC-Okta/issuer_url"
        clientId: "op://Employee-Vault/OIDC-Okta/client_id"
        clientSecret: "op://Employee-Vault/OIDC-Okta/client_secret"
        redirectUrl: "op://Employee-Vault/OIDC-Okta/redirect_url"
    

    Set OP_SERVICE_ACCOUNT_TOKEN in the environment. Secrets resolve in-memory at boot using the 1Password SDK and are never written back to disk or KV storage in plaintext.

  2. Master Key Encryption (PSLENS_MASTER_KEY): Set PSLENS_MASTER_KEY (a 64-character hex AES-256 key). psLens saves passwords in NATS KV and YAML as encrypted strings (ENC[...]).

  3. Environment Variable Overrides: Override passwords dynamically via environment variables without editing files:

    export PSLENS_DB_PROD_PASSWORD="YourPasswordHere"
    

Bare Minimum Configuration Example

To start psLens with zero pre-configured database connections, use the following minimal config.yaml:

server:
  port: 8080

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"
  traceStoreDir: "./data/traces"
  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_PSLENS_SWSPQL/"
    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_PSLENS_SWSPQL/"
    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/..."
    - id: "google-chat-webhook"
      enabled: true
      alertTypes: ["*"]
      databases: ["*"]
      type: "webhook"
      target: "https://chat.googleapis.com/v1/spaces/AAAA.../messages?key=...&token=..."

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",
    "traceStoreDir": "./data/traces",
    "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_PSLENS_SWSPQL/",
      "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_PSLENS_SWSPQL/",
      "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/..."
      },
      {
        "id": "google-chat-webhook",
        "enabled": true,
        "alertTypes": ["*"],
        "databases": ["*"],
        "type": "webhook",
        "target": "https://chat.googleapis.com/v1/spaces/AAAA.../messages?key=...&token=..."
      }
    ]
  },
  "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.

SettingDefaultDescription
port8080TCP port psLens listens on
host0.0.0.0Network interface to bind (use 127.0.0.1 to restrict to localhost)
natsStoreDir./data/natsDirectory for persistent NATS data (report results, alert history)
projectStoreDir./data/projectsDirectory for uploaded PeopleSoft project XML definitions
dmsStoreDir./data/dmsDirectory for imported Data Mover (DMS) script libraries
traceStoreDir./data/tracesDirectory for uploaded PeopleSoft trace files (.tracesql, .trc, .aet)
outageRetentionDays90Retention period in days for historical downtime and outage events stored in NATS JetStream.

Tip: Storage directories (natsStoreDir, traceStoreDir, etc.) can also be configured via environment variables (PSLENS_TRACE_STORE_DIR, PSLENS_PROJECT_STORE_DIR, etc.) and should be placed on persistent storage.


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.

SettingRequiredDescription
nameYesShort identifier shown in the UI (e.g., PROD, DEV)
descriptionYesHuman-readable label for the database
baseURLYesFull URL to the SWS psoftQL endpoint, including the service name
usernameYesPeopleSoft operator ID for API authentication
passwordYesPassword for the operator ID
piaURLNoBase URL for PeopleSoft Internet Architecture (used for deep links to PeopleSoft pages, if supported)
timezoneNoIANA timezone name for the database server (e.g., America/Chicago). Defaults to UTC if not set. Used to interpret timestamps correctly.
downtimesNoList of scheduled downtime windows to suppress alerts and monitoring.
notificationsNoDatabase-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:

SettingRequiredDescription
nameYesIdentifier for the downtime window.
enabledYesWhether the downtime rule is active.
startTimeNoStart time of day in HH:MM format (e.g., 22:00). Set together with endTime.
endTimeNoEnd 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.
daysOfWeekNoArray 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.
startAtNoSpecific start timestamp in RFC3339 format (e.g., 2026-07-04T00:00:00Z) for one-off maintenance.
endAtNoSpecific end timestamp in RFC3339 format (e.g., 2026-07-05T23:59:59Z) for one-off maintenance.

Values for startTime and endTime use wall-clock times in the connection timezone (UTC if unset). psLens checks rules when saving configuration. A rule must define a recurring window (startTime and endTime) or a one-off range (startAt and endAt).

During scheduled downtime, the connection manager sets database status to Downtime on the dashboard. psLens skips background alert checks and suppresses notification dispatches. The database does not count against overall health. When the window ends, psLens checks the connection immediately. It treats the first failed check as an outage. Size windows to cover system boot time.

A weekend window that spans from Saturday evening to Monday morning requires two rules. 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_PSLENS_SWSPQL/

You must include the trailing slash.

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.

For Fly.io and Docker container compatibility, non-alphanumeric characters (such as dashes - or dots .) in database names are automatically converted to underscores (_) and uppercased.

For example, for a database named COOL-DB-FOR-DEV, you can use:

export PSLENS_DB_COOL_DB_FOR_DEV_PASSWORD="my-secure-password"

The raw name PSLENS_DB_COOL-DB-FOR-DEV_PASSWORD remains supported for backward compatibility where supported by your shell.

1Password Secret References

You can place 1Password secret references (op://...) directly in config.yaml or through the Settings UI. Supported fields include:

  • Database Connections: baseURL, username, password, piaURL
  • Database Notifications: notifications.emailTarget, notifications.webhookTarget
  • Database WebLib Health Checks: alerts.checks[].weblibTestTargets[].url, username, password
  • SMTP Mailer: smtp.host, smtp.port, smtp.username, smtp.password, smtp.fromName, smtp.fromEmail
  • Authentication (OIDC): auth.oidc.issuerUrl, auth.oidc.clientId, auth.oidc.clientSecret, auth.oidc.redirectUrl
  • Notifications & Subscriptions: notifications.email_lists[].emails, notifications.webhooks[].url, notifications.subscriptions[].target
databases:
  - name: "PROD"
    baseURL: "op://VaultName/ItemName/base_url"
    username: "PSLENS_API"
    password: "op://VaultName/ItemName/password"

To resolve 1Password secret references at boot, set a 1Password Service Account token in the environment:

export OP_SERVICE_ACCOUNT_TOKEN="op_service_account_token_here"

When you provide OP_SERVICE_ACCOUNT_TOKEN (or PSLENS_OP_SERVICE_ACCOUNT_TOKEN), psLens resolves all op:// references on boot using the 1Password SDK. Without this variable, psLens skips 1Password resolution.

Database-Specific Notifications

By default, global subscription rules route alert notifications for all databases (see Notifications & Webhooks Settings).

You can configure database-specific notifications under the notifications property of a database entry. This setting acts as an override. Database-level targets receive all alerts and resolutions for that database, bypassing global subscriptions.

Each entry in the notifications block supports the following settings:

SettingTypeDescription
emailEnabledBooleanActivates email notifications for this database.
emailTargetStringComma-separated list of recipient email addresses.
webhookEnabledBooleanActivates webhook notifications for this database.
webhookTargetStringWebhook ID or destination URL (Slack, MS Teams, Google Chat, 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_PSLENS_SWSPQL/"
    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

SettingDefaultDescription
enabledfalseWhether to run background alert checks
intervalMinutes5How 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:

SettingDescription
enabledWhether this check is active
thresholdMinutesFor stalled/long-running checks: how many minutes before flagging (default varies by check). For long_running_processes, this is the fallback static threshold.
lookbackHoursFor error checks: how many hours back to look for failures (default varies by check)
excludeProcessesList of process names to skip (for process-related checks)
excludeOperationsList of IB operation names to skip (for Integration Broker checks)
anomalyMultiplierMultiplier applied to the rolling median duration to calculate the dynamic threshold (default: 4.0)
anomalyMinBaselineMinutesMinimum baseline duration in minutes. psLens caps dynamic thresholds to at least this value to prevent false alerts on very fast processes (default: 10)

Available Alert Checks

Check KeyNameDescription
long_running_processesLong-Running ProcessesFlags processes running longer than their rolling median-based expected runtime (or thresholdMinutes fallback)
process_errorsProcess ErrorsFinds processes that failed within lookbackHours (default: 24 hours)
ib_operation_errorsIB Operation ErrorsFinds async IB operations in Error or Timeout status within lookbackHours (default: 24 hours)
ib_pub_contract_errorsIB Publication Contract ErrorsFinds pub contracts in Error or Timeout status within lookbackHours (default: 24 hours)
ib_sub_contract_errorsIB Subscription Contract ErrorsFinds sub contracts in Error or Timeout status within lookbackHours (default: 24 hours)
ib_operation_stalledIB Operations StalledFinds async IB operations stuck in New or Working status longer than thresholdMinutes (default: 30 min)
ib_pub_contract_stalledIB Publication Contracts StalledFinds pub contracts stuck in New or Working status longer than thresholdMinutes (default: 30 min)
ib_sub_contract_stalledIB Subscription Contracts StalledFinds sub contracts stuck in New or Working status longer than thresholdMinutes (default: 30 min)
locked_oprid_processesLocked OPRID Scheduled ProcessesFinds queued or scheduled processes whose submitting OPRID has a locked account
backlogged_processesBacklogged ProcessesDetects processes currently stuck in a Queued or Blocked state longer than thresholdMinutes (default: 30 min)
queue_latencyQueue LatencyDetects processes that experienced a start delay (BEGINDTTM - RUNDTTM) greater than thresholdMinutes (default: 15 min)
failed_loginsFailed LoginsDetects users with excessive failed login attempts in PSPTLOGINAUDIT (defaults to > thresholdCount of 5)
process_run_checkProcess Run CheckMonitors configured critical processes and alerts when they haven’t run successfully within their configured time window
ib_operation_volumeAbnormal IB Operation VolumeDetects when IB operation instance volume exceeds the historical average by a percentage specified in thresholdCount (default: 50)
ib_pub_contract_volumeAbnormal IB Pub Contract VolumeDetects when IB publication contract volume exceeds the historical average by a percentage specified in thresholdCount (default: 50)
ib_sub_contract_volumeAbnormal IB Sub Contract VolumeDetects when IB subscription contract volume exceeds the historical average by a percentage specified in thresholdCount (default: 50)
ib_sync_exceptionsIB Sync Operation ExceptionsDetects synchronous service operations with errors in PSIBLOGHDR within lookbackHours (default: 24 hours). Disabled by default.
no_process_completedNo Process CompletedFires when no process has successfully completed within the lookbackHours (default: 1 hour)
ib_downIntegration Broker DownAlerts when SWS REST endpoint connection failures indicate the Integration Broker is down
weblib_downWeb Server / WebLib DownAlerts when PeopleSoft Web Server is down or configured WebLib URLs fail to respond
ib_no_active_domainIB No Active DomainAlerts when there is no active domain found in PSAPMSGDOMSTAT
ib_dispatcher_downIB Dispatcher DownAlerts when an Integration Broker dispatcher process is inactive or has not updated status within thresholdMinutes (default: 10 min)
ib_nodes_downIB Nodes DownAlerts when there are entries in PSNODESDOWN indicating message nodes are down

Authentication Settings

The auth section configures native authentication (email-based magic link or native OpenID Connect SSO). psLens enables authentication by default. To disable authentication, set enabled: false explicitly in config.yaml. Authentication settings are file-authoritative. You cannot change or turn off authentication from the web interface.

SettingDefaultDescription
enabledtrueDefaults to true. To disable, set to false explicitly in config.yaml.
modemagic_linkAuthentication mode: magic_link or oidc.
authorizedUsers[]Whitelist of email addresses allowed to log in (case-insensitive).
oidc.issuerUrl-OpenID Connect Issuer URL (supports op:// 1Password references).
oidc.clientId-OIDC Application Client ID (supports op:// 1Password references).
oidc.clientSecret-OIDC Application Client Secret (supports op:// 1Password references).
oidc.redirectUrl-Optional custom OAuth2 callback URL (supports op:// 1Password references).
oidc.scopes["openid", "profile", "email"]OIDC authorization scopes requested from the Identity Provider.
oidc.allowedGroups[]Optional list of required group claims to restrict access.
oidc.groupClaimgroupsClaim key in the ID token used for group membership verification.
ipAllowlist.enabledfalseEnable IP allowlist enforcement. Fails closed (denies all) if list is empty.
ipAllowlist.allowedCIDRs[]List of allowed CIDR blocks or IP addresses (e.g. ["10.0.0.0/8", "192.168.1.50"]).

SMTP Settings

Configure email server settings under Settings → Notifications (Email Server card). Changes apply to alert and report emails immediately. Login (magic link) emails use new settings 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 initial boot, settings saved in the UI remain authoritative. PSLENS_SMTP_* environment variables override both sources and make the UI card read-only.

SettingDefaultDescription
host-SMTP server hostname/IP (supports op:// 1Password references)
port-SMTP port (e.g. 25, 465, 587, 2525, supports op:// references)
username-Username for SMTP auth (supports op:// 1Password references)
password-Password for SMTP auth (supports op:// 1Password references)
fromNamepsLensSender name shown in emails (supports op:// 1Password references)
fromEmail-Sender email address for SMTP. Required: no email is sent without it (supports op:// 1Password references)

Test outbound email on the same card: enter a recipient address under Send Test Email and click Send Test. psLens records the test result in Alert Delivery History.


Notifications & Webhooks Settings

Notification rules determine where psLens dispatches alert messages. Manage rules in the app under Settings → Notifications. There, you configure and test email distribution lists, webhook destinations, and subscription routing rules. For step-by-step instructions on creating webhooks in Google Chat, Slack, and Microsoft Teams, see Webhook Integrations.

Email Distribution Lists (notifications.email_lists)

Predefined recipient lists referenced by notifications and reports:

notifications:
  email_lists:
    ops_team:
      name: "Operations Team"
      emails: "ops@company.com, pager@company.com"

Webhooks (notifications.webhooks)

Predefined incoming webhook URLs for Slack, Microsoft Teams Workflows (Adaptive Cards), Google Chat (cardsV2), or custom JSON receivers:

notifications:
  webhooks:
    slack_alerts:
      name: "Slack Alert Channel"
      url: "https://hooks.slack.com/services/..."
    teams_ops:
      name: "Teams Operations Channel"
      url: "https://default...powerautomate.../invoke"
    gchat_ops:
      name: "Google Chat Space"
      url: "https://chat.googleapis.com/v1/spaces/.../messages?key=...&token=..."

To create a Google Chat incoming webhook URL, open the target Space in Google Chat, click the Space name header → Apps & integrations → + Add webhooks, save the webhook, and copy the https://chat.googleapis.com/v1/spaces/.../messages?key=...&token=... link. See Webhook Integrations for full setup instructions.

Subscriptions (notifications.subscriptions)

Routing subscriptions matching fired alerts and dispatching them to email or webhook targets:

PropertyTypeDescription
idStringUnique identifier for the subscription
enabledBooleanActivates or silences the subscription
alertTypesList of StringsAlert check keys to match (e.g. ["*"] for all, or ["process_errors"])
databasesList of StringsDatabase names to match (e.g. ["*"] or ["PROD"])
severityMinStringMinimum alert severity ("info", "warning", "critical")
typeStringDispatch protocol: "email" or "webhook"
targetStringTarget destination: email address, distribution list key, or webhook destination URL / key

Note: psLens automatically detects Slack, MS Teams Adaptive Card, and Google Chat webhook URLs and formats native payloads accordingly.


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:

PropertyTypeDescription
idStringThe unique report definition ID (e.g. security-full-access-permlists)
enabledBooleanEnables or disables the scheduled execution of this report (defaults to true)
dbNamesList of StringsDatabase names to target (e.g., ["PROD"]). If omitted, targets all active databases.
scheduleObjectTiming recurrence properties (see below)
emailTargetStringOptional email address to send report completion notifications to
webhookTargetStringOptional Slack, MS Teams, Google Chat, or JSON webhook endpoint to send notifications to

Schedule Recurrence Properties

Under reports.schedule, define when the job should run:

PropertyTypeDescription
intervalStringRecurrence frequency: "daily", "weekly", or "monthly"
timeOfDayString24-hour format execution time ("HH:MM", e.g., "02:00") in server local time
dayOfWeekIntegerOptional. Day of the week for weekly runs (0 for Sunday, 1 for Monday, …, 6 for Saturday)
dayOfMonthIntegerOptional. 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.

  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 authentication. Enable built-in magic-link auth or place an authenticated reverse proxy in front of psLens. Unprotected instances expose read access to PeopleSoft metadata.


Active Configuration and Hot-Reloading

psLens keeps its settings in two places:

  1. Active configuration (embedded key-value store): Web interface edits save here and take effect immediately without server restarts. psLens records each change as a numbered revision under Settings → Change History. You can view or restore any revision.
  2. config.yaml on 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 you set the environment variable PSLENS_FORCE_SEED_CONFIG=true, psLens seeds 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. This overwrites active configuration with file contents and discards changes made in the UI.
  • If the file path is writable, click Overwrite config.yaml on disk to write active configuration to disk. On ephemeral deployment models without persistent volumes, restarting or re-deploying the container discards local file changes.
  • Alternatively, copy the Active Configuration YAML from the Advanced page into your config.yaml file to bring the disk file in sync.

Secret Key Management (Encryption at Rest)

When you configure the PSLENS_MASTER_KEY environment variable with a 32-byte hex-encoded key, psLens encrypts database and SMTP passwords at rest. Without this master key, psLens saves passwords in plaintext.

Rotating the Master Key

To rotate the cryptographic key used for credential encryption:

  1. Enter a new 32-byte hex key in the Rotate Encryption Key field.
  2. 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.
  3. Update the PSLENS_MASTER_KEY environment variable in your deployment configuration (e.g., Fly.io secrets or .env file) to match the new key before restarting the container. If the container restarts with a mismatched key, it cannot decrypt the configuration.