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:
parent
7bfd6997ec
commit
2f43d67af9
5 changed files with 43 additions and 144 deletions
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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`)
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue