AppSpace Bot for monitoring Digital Signage
Find a file
jmcqueen fc5a76b9b8 Initial commit: Appspace + Webex alerting bot
Node/Express service that:
- Receives Appspace outbound webhooks, enriches with Workspace ONE MDM
  data (matched by serial), and posts Adaptive Card alerts to Webex.
- Runs a Webex bot in WebSocket mode with two commands:
    * `offline [filter]`  - lists currently offline / lost / failed
      Appspace devices, enriched with per-device MDM facts + console links.
    * `restart-offline [filter]` - sends WS1 SoftReset (reboot) to every
      currently-offline device that has a WS1 record. Capped at 50 per
      invocation with bounded concurrency to protect the WS1 API.

Notes on hardening already applied:
- In-flight promise coalescing in mdm.js and index.js so burst webhook
  traffic can't stampede the WS1 token / device-cache refresh or the
  Appspace token refresh.
- Structured logger that serializes Error instances (message, stack,
  code, axios response.status/data) instead of stringifying to "{}".
- Webex 7439-char message-limit handling: `offline` builds its body
  incrementally against a character budget and reports accurate
  "N more not shown" truncation.
- Uses string phrases for `framework.hears(...)` so the framework's
  `(^| )phrase($| )` wrapper handles group-space @mentions correctly,
  and a shared `extractFilterArg()` helper so filter parsing works
  identically in DMs and mentioned messages.

Config, Docker, smoke-test profile, and healthcheck included.
Secrets are managed via `.env` (gitignored); see `.env.example`.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-01 17:32:44 -04:00
.dockerignore Initial commit: Appspace + Webex alerting bot 2026-07-01 17:32:44 -04:00
.env.example Initial commit: Appspace + Webex alerting bot 2026-07-01 17:32:44 -04:00
.gitignore Initial commit: Appspace + Webex alerting bot 2026-07-01 17:32:44 -04:00
docker-compose.yml Initial commit: Appspace + Webex alerting bot 2026-07-01 17:32:44 -04:00
Dockerfile Initial commit: Appspace + Webex alerting bot 2026-07-01 17:32:44 -04:00
index.js Initial commit: Appspace + Webex alerting bot 2026-07-01 17:32:44 -04:00
mdm.js Initial commit: Appspace + Webex alerting bot 2026-07-01 17:32:44 -04:00
package-lock.json Initial commit: Appspace + Webex alerting bot 2026-07-01 17:32:44 -04:00
package.json Initial commit: Appspace + Webex alerting bot 2026-07-01 17:32:44 -04:00
query-offline.js Initial commit: Appspace + Webex alerting bot 2026-07-01 17:32:44 -04:00
README.md Initial commit: Appspace + Webex alerting bot 2026-07-01 17:32:44 -04:00

Appspace Webex Alerts

Lightweight Node/Express service that bridges Appspace device health events to Cisco Webex via Adaptive Cards, with optional enrichment from Workspace ONE MDM.

It also runs an interactive Webex bot (WebSocket mode) for on-demand queries.

What it does

  • Listens for Appspace outbound webhooks (DEVICE.HEALTHSTATUS.* and DEVICE.UNREGISTERED).
  • Ignores PWA devices.
  • Enriches alerts with current MDM data (model, OS version, compliance, last sample/seen timestamps in EDT).
  • Posts formatted Adaptive Cards to a configured Webex room (with direct links to both consoles).
  • Provides a Webex bot with commands:
    • offline [optional filter] — snapshot of currently offline/lost/failed devices (client-side filter on name or type).
    • help

Requirements

  • Node 20+
  • Appspace instance with outbound webhook + Application refresh token
  • Webex bot token + room ID
  • (Optional but recommended) Workspace ONE MDM OAuth client for enrichment
  1. Copy env:

    cp .env.example .env
    # or .env.dev for the dev profile
    
  2. Fill in the required values (see .env.example for descriptions).

  3. Run:

    # Production profile
    npm run docker:prod
    
    # Development (with live reload + volume mount)
    npm run docker:dev
    
    # Smoke test (builds image + verifies /health responds "healthy" inside container)
    npm run docker:smoke
    

Port mapping notes: The container always listens internally on port 3000 (hardened default). Host port 1889 is used for both dev (docker compose --profile dev up -d app-dev) and prod (docker compose up -d). The PORT env inside the container is forced to 3000 via compose. You cannot run both profiles at the same time due to the shared host port.

Direct:

npm install
npm start

Health check: GET /health

Environment Variables

See .env.example for the full documented list.

Key ones:

  • WEBEX_BOT_TOKEN, WEBEX_ROOM_ID
  • APPSPACE_INSTANCE_URL, APPSPACE_SUBJECT_ID, APPSPACE_REFRESH_TOKEN, APPSPACE_API_BASE_URL
  • WS1_* (for MDM enrichment)
  • WEBHOOK_SECRET (recommended for the Appspace webhook)
  • DEBUG, DEBUG_WEBHOOK (see below)

Bot Commands

In the Webex space where the bot is added:

  • offline — current problematic devices
  • offline tablet — filter to devices whose name or type contains "tablet"
  • help

The bot runs in WebSocket mode (no public webhook required).

Debugging

  • DEBUG=true — verbose logging for MDM lookups, ignored events, command handling, etc. (very useful in dev, noisy in prod).
  • DEBUG_WEBHOOK=true — log the full incoming Appspace webhook payload (contains device details; do not leave on in production).
  • LOG_FORMAT=json (or NODE_ENV=production) — output structured JSON logs (ideal for Docker/K8s log collectors).

You can also set NODE_ENV=development for similar verbose behavior.

Production & Docker Notes

  • Graceful shutdown: The service handles SIGTERM (used by docker stop, Kubernetes, etc.) and SIGINT. It will:
    1. Stop the Webex WebSocket framework (important to avoid "excessive device registrations").
    2. Close the HTTP server.
    3. Exit cleanly. A hard timeout forces exit after ~8s.
  • Healthcheck: /health returns 200 with basic status. Used by Docker and orchestrators.
  • Logging: Logs go to stdout/stderr (12-factor / Docker friendly). Use LOG_FORMAT=json or NODE_ENV=production for structured JSON. Use DEBUG=true in non-prod for detail. Pipe to a collector (Loki, CloudWatch, etc.) as needed.
  • Secrets: Never bake secrets into the image. Use:
    • env_file for compose (dev/staging only)
    • Docker secrets, Kubernetes Secrets, or a secrets manager (Vault, AWS Secrets Manager) for production.
  • Ports: Container always listens on 3000 internally. Map host ports as needed (see docker-compose.yml).
  • Non-root: Production image runs as the node user.
  • Resources: In production, set CPU/memory limits in your orchestrator. The bot command does a full device list scan (limit 500) — monitor for large fleets.

Architecture Notes

  • Appspace token uses refresh token + cooldown + safety buffer.
  • MDM uses 24h serial→ID cache + fresh detail lookup by ID on every alert (for up-to-date compliance/last-seen).
  • WebSocket mode for the bot avoids restart rate limits.
  • All enrichment is best-effort; alerts are never blocked by MDM or token issues.

License / Support

Internal tool. Tweak as needed.

TODO / Future

  • Server-side filtering for the devices list when the Appspace API supports it reliably.
  • Metrics / full structured JSON logging.
  • Support for more Appspace event types.
  • Multi-stage Dockerfile for even smaller prod images.