# PULSE - Pipelined Unified Logic & Server Engine [![Lint](https://code.lotusguild.org/LotusGuild/pulse/actions/workflows/lint.yml/badge.svg)](https://code.lotusguild.org/LotusGuild/pulse/actions?workflow=lint.yml) [![Test](https://code.lotusguild.org/LotusGuild/pulse/actions/workflows/test.yml/badge.svg)](https://code.lotusguild.org/LotusGuild/pulse/actions?workflow=test.yml) [![Security](https://code.lotusguild.org/LotusGuild/pulse/actions/workflows/security.yml/badge.svg)](https://code.lotusguild.org/LotusGuild/pulse/actions?workflow=security.yml) A distributed workflow orchestration platform for managing and executing complex multi-step operations across server clusters through a retro terminal-themed web interface. > **Security Notice:** This repository is hosted on Gitea and is version-controlled. **Never commit secrets, credentials, passwords, API keys, or any sensitive information to this repo.** All sensitive configuration belongs exclusively in `.env` files which are listed in `.gitignore` and must never be committed. This includes database passwords, worker API keys, webhook secrets, and internal IP details. **Design System**: [web_template](https://code.lotusguild.org/LotusGuild/web_template) — shared CSS, JS, and layout patterns for all LotusGuild apps ## Styling & Layout PULSE uses the **LotusGuild Terminal Design System**. The design system is **vendored** into this repo at `public/web_template/` (`base.css`, `base.js`, `VERSION`) and served from `/web_template/*`, so the app has no runtime dependency on a sibling checkout. `public/web_template/VERSION` records the design-system version, upstream short SHA, and sync date; asset URLs are cache-busted with it. Update the vendored copy with: ```bash scripts/sync-web-template.sh ``` The script copies `base.css`/`base.js` as regular files (never symlinks) and rewrites `VERSION`. Never hand-edit `public/web_template/` — it is excluded from ESLint and overwritten on every sync. Pulse-local gaps in the design system live in `public/assets/app.css` (currently `.lt-modal-lg`, `.lt-field-error`, `.is-invalid`) and are candidates for upstreaming. Reference documentation: - [`web_template/README.md`](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/README.md) — full component reference, CSS variables, JS API - [`web_template/base.css`](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/base.css) — unified CSS (`.lt-*` classes) - [`web_template/base.js`](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/base.js) — `window.lt` utilities (toast, modal, WebSocket helpers, fetch) - [`web_template/aesthetic_diff.md`](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/aesthetic_diff.md) — cross-app divergence analysis and convergence guide - [`web_template/node/middleware.js`](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/node/middleware.js) — Express auth, CSRF, CSP nonce middleware ## Web UI The UI is server-rendered with EJS. Every route renders `views/pages/.ejs` into the shared chrome in `views/layout.ejs` (nav, header, WebSocket status dot, theme toggle, command palette). | Route | Page | View | Page module | |---|---|---|---| | `/` | Dashboard | `views/pages/dashboard.ejs` | `public/assets/pages/dashboard.js` | | `/workers` | Workers | `views/pages/workers.ejs` | `public/assets/pages/workers.js` | | `/workflows` | Workflows | `views/pages/workflows.ejs` | `public/assets/pages/workflows.js` | | `/executions` | Executions | `views/pages/executions.ejs` | `public/assets/pages/executions.js` | | `/quick` | Quick Command | `views/pages/quick.ejs` | `public/assets/pages/quick.js` | | `/scheduler` | Scheduler | `views/pages/scheduler.ejs` | `public/assets/pages/scheduler.js` | Scripts load in a fixed order: `/web_template/base.js` → `/assets/app.js` → `/assets/pages/.js`. `app.js` owns the shell (`window.Pulse`: action registry, event bus, `Pulse.confirm`, formatters, WebSocket, the single 30 s auto-refresh) and each page module registers itself with: ```js Pulse.registerPage({ name, init(), refresh(), onEvent(type, data) /* return true if handled */ }); ``` Page modules never attach their own listeners for UI actions — they register handlers under their own action prefix (`dash:`, `wk:`, `wf:`, `ex:`, `qc:`, `sc:`) and the markup wires them up with `data-action` / `data-change-action` / `data-input-action` / `data-submit-action` attributes that `app.js` delegates. DOM ids are likewise prefixed per page. All dynamic strings go through `Pulse.esc`, and destructive actions use the themed `Pulse.confirm` (no native `confirm()`/`alert()`). **Content Security Policy:** pages are served under a strict nonce-based CSP (helmet), including `script-src-attr 'none'`. There are **no inline `