docs: simplify Mermaid diagrams for better readability

The previous diagrams were too complex and overwhelming. Simplified
all diagrams to show core concepts clearly:

- Adaptive polling: reduced to basic scheduler→queue→workers flow
- Temperature proxy: simplified to 3-box trust boundary view
- Sensor proxy sequence: simplified to essential request flow
- Webhook pipeline: reduced to template→send→retry flow
- Script library: simplified to code→test→bundle→dist flow

Fixed parsing error in temperature proxy diagram (parentheses in
edge label causing render failure).

Diagrams should clarify architecture, not recreate implementation.
This commit is contained in:
rcourtman 2025-10-21 10:50:40 +00:00
parent 7bfd6997ec
commit 2f43d67af9
5 changed files with 43 additions and 144 deletions

View file

@ -19,37 +19,18 @@ This document describes the security architecture of Pulse's temperature monitor
```mermaid ```mermaid
graph TD graph TD
subgraph Host["Proxmox Host (delly)\nTrust Boundary"] Container[Pulse Container]
Proxy["pulse-sensor-proxy service\nUID 999\nSO_PEERCRED auth\nMethod ACL + per-UID rate limit\nPer-node concurrency = 1"] Proxy[pulse-sensor-proxy<br/>Host Service]
Socket["Unix socket\n/run/pulse-sensor-proxy.sock\n(0600 bind mount)"] Cluster[Cluster Nodes<br/>SSH sensors -j]
Audit["Audit & Metrics\n/var/log/pulse/... & :9127/metrics"]
PrivOps["Privileged RPCs\nensure_cluster_keys | register_nodes | request_cleanup\nHost UID only"]
end
subgraph Container["Pulse Container (ID-mapped root)"] Container -->|Unix Socket<br/>Rate Limited| Proxy
Backend["Pulse Backend"] Proxy -->|SSH<br/>Forced Command| Cluster
Poller["Temperature Poller worker"] Cluster -->|Temperature JSON| Proxy
end Proxy -->|Temperature JSON| Container
subgraph Cluster["Cluster Nodes"] style Proxy fill:#e1f5e1
SensorCmd["Forced SSH command\n`sensors -j` only\nRestricted authorized_keys entry"] style Container fill:#fff4e1
end style Cluster fill:#e1f0ff
Poller -->|poll request| Backend
Backend -->|RPC via bind-mounted socket| Socket
Socket --> Proxy
Proxy -->|temperature JSON response| Backend
Proxy -->|rate-limit reject + 2 s penalty| Reject["429 response"]
Reject --> Backend
Proxy -->|SSH (ed25519 key)\nforced command| SensorCmd
SensorCmd -->|temperature JSON| Proxy
Proxy -->|audit entry + metrics| Audit
Audit -->|Prometheus scrape| Metrics["Telemetry Consumers\n(Grafana, watchdog)"]
PrivOps --> Proxy
Backend -. blocked (ID-mapped root) .-> PrivOps
``` ```
**Key Principle**: SSH keys never enter containers. All SSH operations are performed by the host-side proxy. **Key Principle**: SSH keys never enter containers. All SSH operations are performed by the host-side proxy.

View file

@ -114,31 +114,15 @@ For webhooks that require authentication or custom headers:
## Custom Payload Templates ## Custom Payload Templates
```mermaid ```mermaid
flowchart TD flowchart LR
AlertEvent["Alert Event Triggered"] Alert[Alert Triggered]
GatherData["Gather Alert Data\n(Level, Type, Resource, Node, etc.)"] Template[Render Template<br/>Variables & Functions]
ResolveURL["Resolve URL Template\n({{urlpath}}, {{urlquery}}"] Send[HTTP POST]
ResolvePayload["Resolve Payload Template\n(variable substitution)"]
ApplyFunctions["Apply Template Functions\n(title, upper, lower, printf)"]
Dispatch["HTTP POST Request"]
CheckResponse{"Response\nStatus?"}
Success["200-299: Success\nLog delivery"]
Retry["429/5xx: Retry\n(exponential backoff)"]
Failure["4xx: Failure\nLog error"]
TrackDelivery["Update Delivery Metrics\npulse_webhook_deliveries_total"]
AlertEvent --> GatherData Alert --> Template
GatherData --> ResolveURL Template --> Send
ResolveURL --> ResolvePayload Send -->|2xx| Success[Delivered]
ResolvePayload --> ApplyFunctions Send -->|5xx| Retry[Retry with Backoff]
ApplyFunctions --> Dispatch
Dispatch --> CheckResponse
CheckResponse -->|Success| Success
CheckResponse -->|Transient Error| Retry
CheckResponse -->|Permanent Error| Failure
Success --> TrackDelivery
Retry --> TrackDelivery
Failure --> TrackDelivery
``` ```
For generic webhooks, you can define custom JSON payloads using Go template syntax. For generic webhooks, you can define custom JSON payloads using Go template syntax.

View file

@ -4,45 +4,16 @@
Pulse uses an adaptive polling scheduler that adapts poll cadence based on freshness, errors, and workload. The goal is to prioritize stale or changing instances while backing off on healthy, idle targets. Pulse uses an adaptive polling scheduler that adapts poll cadence based on freshness, errors, and workload. The goal is to prioritize stale or changing instances while backing off on healthy, idle targets.
```mermaid ```mermaid
flowchart TD flowchart LR
PollLoop["PollLoop\n(ticker & config updates)"] Scheduler[Scheduler]
Scheduler["Scheduler\ncomputes ScheduledTask"] Queue[Priority Queue<br/>by NextRun]
Staleness["Staleness Tracker\n(last success, freshness score)"] Workers[Workers]
CircuitBreaker["Circuit Breaker\ntracks failure streaks"]
Backoff["Backoff Policy\nexponential w/ jitter"]
PriorityQ["Priority Queue\nmin-heap by NextRun"]
WorkerPool["TaskWorkers\nN concurrent workers"]
Metrics["Metrics & History\nPrometheus + retention"]
Success["Poll Success"]
Failure{"Poll Failure?"}
Reschedule["Reschedule\n(next interval)"]
BackoffPath["Backoff / Breaker Open"]
DeadLetter["Dead-Letter Queue\noperator review"]
PollLoop --> Scheduler Scheduler -->|schedule| Queue
Staleness --> Scheduler Queue -->|dequeue| Workers
CircuitBreaker --> Scheduler Workers -->|success| Scheduler
Scheduler --> PriorityQ Workers -->|failure| CB[Circuit Breaker]
CB -->|backoff| Scheduler
PriorityQ -->|due task| WorkerPool
WorkerPool --> Failure
WorkerPool -->|result| Metrics
WorkerPool -->|freshness| Staleness
Failure -->|No| Success
Success --> CircuitBreaker
Success --> Reschedule
Success --> Metrics
Reschedule --> Scheduler
Failure -->|Yes| BackoffPath
BackoffPath --> CircuitBreaker
BackoffPath --> Backoff
Backoff --> Scheduler
Backoff --> DeadLetter
DeadLetter -. periodic retry .-> Scheduler
CircuitBreaker -. state change .-> Scheduler
Metrics --> Scheduler
``` ```
- **Scheduler** computes `ScheduledTask` entries using adaptive intervals. - **Scheduler** computes `ScheduledTask` entries using adaptive intervals.

View file

@ -12,36 +12,15 @@
```mermaid ```mermaid
sequenceDiagram sequenceDiagram
participant Backend as Pulse Backend participant Backend
participant Proxy as Sensor Proxy RPC Server participant Proxy
participant Limiter as Limiter (per UID & global) participant Node
participant Validator as Payload Validator
participant SSH as Cluster Node (forced `sensors -j`)
participant Metrics as Metrics & Audit Log
Backend->>Proxy: RPC request (get_temperature) Backend->>Proxy: get_temperature
Proxy->>Proxy: Extract SO_PEERCRED (UID/GID/PID) Proxy->>Proxy: Check rate limit
Proxy->>Limiter: Check per-UID rate & concurrency Proxy->>Node: SSH sensors -j
alt Rate limit exceeded Node->>Proxy: JSON response
Limiter-->>Proxy: reject Proxy->>Backend: Temperature data
Proxy-->>Backend: 429 Too Many Requests (2 s penalty)
Proxy->>Metrics: increment limiter_rejections_total
else Allowed
Limiter-->>Proxy: permit
Proxy->>Validator: Validate method & payload
alt Validation failure
Validator-->>Proxy: error
Proxy-->>Backend: 400 validation error
Proxy->>Metrics: penalty + audit log entry
else Valid request
Validator-->>Proxy: ok
Proxy->>SSH: run `sensors -j` via forced command
SSH-->>Proxy: temperature JSON
Proxy-->>Backend: telemetry payload
Proxy->>Metrics: record success, latency histogram
Proxy->>Metrics: append audit/audit trail
end
end
``` ```
### Rate Limit Hits (`pulse_proxy_limiter_rejections_total`) ### Rate Limit Hits (`pulse_proxy_limiter_rejections_total`)

View file

@ -34,31 +34,15 @@ dist/ # Generated bundled scripts
### Development & Bundling Workflow ### Development & Bundling Workflow
```mermaid ```mermaid
flowchart TD flowchart LR
Author["Author Code\nscripts/lib/*.sh\nscripts/install-*.sh"] Code[Write Code<br/>scripts/lib]
WriteTests["Write Tests\nscripts/tests/test-*.sh\nscripts/tests/integration/"] Test[Run Tests]
UpdateManifest["Update Bundle Manifest\nscripts/bundle.manifest"] Bundle[Bundle<br/>make bundle-scripts]
RunTests["Run Tests\nmake test-scripts\nscripts/tests/run.sh"] Dist[Distribute<br/>dist/*.sh]
TestPass{"Tests Pass?"}
FixCode["Fix Issues"]
Bundle["Bundle Scripts\nmake bundle-scripts\nbash scripts/bundle.sh"]
ValidateBundled["Validate Bundled Output\nbash -n dist/*.sh\ndist/*.sh --dry-run"]
ValidatePass{"Validation\nPass?"}
Distribute["Distribute\ndist/*.sh ready"]
UpdateDocs["Update Documentation\nscripts/lib/README.md"]
Author --> WriteTests Code --> Test
WriteTests --> UpdateManifest Test --> Bundle
UpdateManifest --> RunTests Bundle --> Dist
RunTests --> TestPass
TestPass -->|No| FixCode
FixCode --> Author
TestPass -->|Yes| Bundle
Bundle --> ValidateBundled
ValidateBundled --> ValidatePass
ValidatePass -->|No| FixCode
ValidatePass -->|Yes| UpdateDocs
UpdateDocs --> Distribute
``` ```
This workflow emphasizes the library's modular design: develop reusable modules in `scripts/lib`, test thoroughly, bundle for distribution, and validate bundled artifacts before release. This workflow emphasizes the library's modular design: develop reusable modules in `scripts/lib`, test thoroughly, bundle for distribution, and validate bundled artifacts before release.