Cross-platform deployment monitoring CLI. Track deployments across GitHub Actions, Railway, and Vercel from a single terminal.
Pulsos is a terminal tool that gives you a unified view of deployments across GitHub Actions, Railway, and Vercel. It runs as a live TUI dashboard, as a persistent background daemon with a native system-tray icon, or as one-shot CLI output. The correlation engine matches deployment events across platforms by commit SHA and timestamp heuristics, so you can see how a single commit flows through your CI/CD pipeline.
- Live TUI with 5 tabs: Unified, Platform, Health, Settings, Logs
- Cross-platform correlation — matches events by SHA and timestamp proximity
- Health scoring per project (weighted: GitHub 40%, Railway 35%, Vercel 25%)
- DORA metrics — Deployment Frequency, Lead Time, Change Failure Rate, Time to Restore
- Real-time telemetry — Railway container metrics (CPU/RAM) and blackbox endpoint pings (TTFB/uptime)
- Background daemon —
pulsos daemon startruns a persistent process with an Axum SSE server and native tray icon (macOS / Windows) - Auto-discovery of repos, projects, and services via
repos sync - Saved views with filters for project, platform, branch, and status
- ETag caching with rate-limit-aware adaptive polling
- Credential storage — OS keyring with file fallback (
~/.config/pulsos/credentials.toml) - Output formats — table, compact, JSON
- Shell completions for bash, zsh, fish, powershell, elvish
cargo install pulsos-cligit clone https://github.com/Vivallo04/pulsos-cli.git
cd pulsos-cli
cargo install --path crates/pulsos-clibrew install Vivallo04/tap/pulsos- Rust 1.75+
# 1. Authenticate with your platforms
pulsos auth github
pulsos auth railway
pulsos auth vercel
# 2. Discover and track your repos/projects
pulsos repos sync
# 3. Launch the live TUI dashboard
pulsos status --watch
# Or get a one-shot table in your terminal
pulsos status --once
# Or run as a persistent background daemon
pulsos daemon startWhat happens at each step:
pulsos auth <platform>prompts for a token and stores it in your OS keyring (falls back to~/.config/pulsos/credentials.toml).pulsos repos syncqueries each authenticated platform, discovers available repos/projects/services, lets you pick which ones to track, and auto-generates correlations.pulsos statusfetches recent deployments from all tracked resources, correlates them, and displays the result. With--watchit launches the live TUI; without flags it auto-detects (TUI if interactive terminal, one-shot otherwise).pulsos daemon startdetaches the daemon to the background. The TUI automatically connects to the daemon's SSE stream when one is running, saving API quota.
Pulsos resolves tokens in this priority order:
- Environment variables — checked first
- OS keyring — via
keyringcrate (macOS Keychain, Windows Credential Manager, Linux Secret Service) - CLI config detection — reads tokens from
gh,railway, andvercelCLI config files
| Platform | Variables |
|---|---|
| GitHub | GH_TOKEN, GITHUB_TOKEN |
| Railway | RAILWAY_TOKEN, RAILWAY_API_TOKEN |
| Vercel | VERCEL_TOKEN |
pulsos auth github # Authenticate with GitHub
pulsos auth railway # Authenticate with Railway
pulsos auth vercel # Authenticate with VercelEach command prompts for a token, validates it against the platform API, and stores it.
# Store tokens in one shot (CI mode)
pulsos auth --ci --github-token=ghp_xxx --railway-token=xxx --vercel-token=xxx
# Check which tokens are resolved
pulsos auth status
# Re-validate stored tokens; re-prompts if invalid
pulsos auth refresh
# Remove stored tokens
pulsos auth logout
pulsos auth logout --platform githubYou can disable automatic detection of specific sources in config.toml:
[auth.token_detection]
detect_gh_cli = true
detect_railway_cli = true
detect_vercel_cli = true
detect_env_vars = trueConfig file location: ~/.config/pulsos/config.toml
Run pulsos config path to print the exact path, or pulsos config edit to open it in $EDITOR.
[auth]
github_host = "github.mycompany.com" # Default: "github.com"
[auth.token_detection]
detect_gh_cli = true
detect_railway_cli = false
detect_vercel_cli = true
detect_env_vars = true
[[github.organizations]]
name = "myorg"
include_patterns = ["api-*"]
exclude_patterns = ["*-legacy"]
auto_discover = true
[[railway.workspaces]]
name = "lambda-prod"
include_projects = ["my-saas-api"]
default_environment = "production" # Default: "production"
[[vercel.teams]]
name = "lambda"
include_projects = ["my-saas-web"]
include_preview_deployments = true
[[correlations]]
name = "my-saas"
github_repo = "myorg/my-saas"
railway_project = "my-saas-api"
vercel_project = "my-saas-web"
[correlations.branch_mapping]
main = "production"
develop = "staging"
[[views]]
name = "production"
description = "Production systems"
projects = ["my-saas", "api-core"]
platforms = ["github", "railway", "vercel"]
branch_filter = "main"
status_filter = ["success", "failure"]
refresh_interval = 5
[[groups]]
name = "backend"
resources = ["api-core", "auth-service"]
[tui]
theme = "dark" # "dark" or "light"
fps = 10 # Render frames per second
refresh_interval = 5 # Seconds between API polls
default_tab = "unified" # "unified", "platform", "health", "settings", "logs"
show_sparklines = true
unicode = "auto" # "auto", "always", "never"
[cache]
max_size_mb = 100| Section | Purpose |
|---|---|
[auth] |
GitHub Enterprise host, token detection toggles |
[[github.organizations]] |
Org name, include/exclude repo patterns |
[[railway.workspaces]] |
Workspace name, include/exclude projects, default environment |
[[vercel.teams]] |
Team name, include projects, preview deployment toggle |
[[correlations]] |
Link a GitHub repo, Railway project, and Vercel project under one name |
[[views]] |
Named filter presets (projects, platforms, branch, status) |
[[groups]] |
Named collections of resources for UI-level grouping |
[tui] |
Theme, FPS, refresh interval, default tab, sparklines |
[cache] |
Max cache size in MB |
pulsos # Default: show deployment status
pulsos status # Same as above
pulsos status --watch # Live TUI mode
pulsos status --once # Force one-shot output
pulsos status --platform github # Filter by platform
pulsos status --view production # Use a saved view
pulsos status --branch main # Filter by branch
pulsos status --format json # JSON output
pulsos status --format compact # Compact outputpulsos auth github # Authenticate with GitHub
pulsos auth railway # Authenticate with Railway
pulsos auth vercel # Authenticate with Vercel
pulsos auth github --token ghp_xxx # Non-interactive with token
pulsos auth status # Check auth status across platforms
pulsos auth refresh # Re-validate tokens, re-prompt if invalid
pulsos auth refresh --platform github # Refresh a specific platform
pulsos auth logout # Remove tokens (prompts for platform)
pulsos auth logout --platform github # Remove a specific platform token
pulsos auth --ci --github-token=xxx # CI mode: store tokens non-interactively
pulsos auth --from-env # Only check env vars (skip keyring/interactive)pulsos repos sync # Discover, select, and save (all platforms)
pulsos repos list # Show tracked repos/projects
pulsos repos add github:org/repo # Add a resource
pulsos repos remove github:org/repo # Remove a resource
pulsos repos correlate my-saas # Edit correlations for a project
pulsos repos groups list # List resource groups
pulsos repos groups create mygroup -- github:org/repo railway:project
pulsos repos groups delete mygroup # Delete a group
pulsos repos verify # Check access permissions for tracked resourcespulsos views # List all views (default)
pulsos views list # List all views
pulsos views show production # Display view details
pulsos views create # Create a view interactively
pulsos views edit production # Edit a view interactively
pulsos views delete production # Delete a view
pulsos views templates # List built-in templates
pulsos views validate production # Validate view projects against correlations
pulsos views export production -o view.json # Export to JSON
pulsos views import view.json # Import from JSONpulsos config # Print current config as TOML (default)
pulsos config show # Print current config as TOML
pulsos config path # Print config file path
pulsos config edit # Open config in $EDITOR
pulsos config wizard # Run interactive platform setup wizardpulsos daemon run # Run the daemon in the foreground (owns main thread for tray icon)
pulsos daemon start # Start the daemon as a detached background process
pulsos daemon stop # Stop the running daemon (sends SIGTERM via PID file)
pulsos daemon status # Print daemon running state and portDaemon architecture:
daemon runstarts an Axum SSE server on an ephemeral port, writes the port to~/.config/pulsos/daemon.port, generates a random 64-char bearer token at~/.config/pulsos/daemon.token(mode 0600 on Unix), and (on macOS/Windows) shows a native tray icon in the menu bar.daemon startre-execsdaemon runas a detached process (stdin/stdout/stderr closed).daemon stopreads~/.config/pulsos/daemon.pidand sends SIGTERM (Unix) ortaskkill /F(Windows), then removesdaemon.pid,daemon.port, anddaemon.token.daemon statuschecks the/healthendpoint of the running daemon.- When a daemon is running,
pulsos status --watchconnects to its bearer-auth-protected SSE stream (/api/stream) instead of polling platform APIs directly, conserving rate-limit budget. The/healthendpoint remains public (used bydaemon status). - The tray icon shows three states: neutral (all OK), syncing (blue), alert (red — endpoint down or deployment failure on main/master).
- Desktop notifications fire on endpoint state transitions (up→down, down→up) on macOS and Windows.
pulsos doctor # Run diagnostics (system, auth, connectivity, cache)
pulsos completions bash # Generate shell completions
pulsos completions zsh
pulsos completions fish
pulsos completions powershell
pulsos completions elvish| Flag | Description |
|---|---|
--format <table|compact|json> |
Output format (default: table) |
--no-color |
Disable color output |
--verbose |
Show debug information |
--config <path> |
Custom config file path |
| Key | Action |
|---|---|
1–5 |
Switch to tab (Unified, Platform, Health, Settings, Logs) |
Tab / Shift+Tab |
Cycle tabs forward / backward |
j / k or ↑ / ↓ |
Navigate rows |
/ |
Enter search mode |
Enter |
Apply search (in search mode) |
Esc |
Exit search mode / cancel |
r |
Force refresh |
s |
Cycle sort order on Unified tab (by time / by platform) |
q / Ctrl+C |
Quit |
| Key | Action |
|---|---|
← / → |
Switch provider subtab (GitHub / Railway / Vercel) |
g |
Switch to GitHub subtab |
w |
Switch to Railway subtab |
v |
Switch to Vercel subtab |
d |
Toggle GitHub job/step details panel (GitHub subtab only) |
| Key | Action |
|---|---|
↑ / ↓ or j / k |
Navigate tree / scroll right panel |
→ / Enter |
Expand job or open right panel |
← |
Collapse job or focus left tree |
PageDown / PageUp |
Scroll right panel by 20 lines |
Ctrl+D / Ctrl+U |
Scroll right panel by 10 lines |
Esc |
Close details panel |
| Key | Action |
|---|---|
f |
Cycle log level filter (ALL → ERR → WARN → INFO → ALL) |
c |
Copy selected log message to clipboard |
| Key | Action |
|---|---|
t |
Enter token for selected platform |
T |
Enter token (force override even with env token) |
v |
Validate selected platform |
x |
Remove stored token |
o / Enter |
Start onboarding / discovery flow |
pulsos-cli/
├── crates/
│ ├── pulsos-core/ # Library crate
│ │ └── src/
│ │ ├── platform/ # GitHub, Railway, Vercel API clients + retry helper
│ │ ├── correlation/ # Event matching engine (SHA + timestamp heuristic)
│ │ ├── domain/ # DeploymentEvent, CorrelatedEvent, DoraMetrics,
│ │ │ # ResourceMetrics, EndpointHealth, health scoring
│ │ ├── auth/ # Credential resolution, KeyringStore, FileCredentialStore, FallbackStore
│ │ ├── config/ # TOML config loading and saving
│ │ ├── cache/ # sled-based ETag cache
│ │ ├── health/ # Platform health checks (PlatformHealthReport) + PingEngine
│ │ ├── scheduler/ # Polling budget and adaptive scheduler
│ │ ├── analytics/ # DoraCalculator
│ │ └── sync/ # Auto-correlation builder (build_correlations, merge)
│ ├── pulsos-cli/ # Binary crate
│ │ ├── assets/ # Embedded tray icon PNGs (neutral, sync, alert — 16x16)
│ │ └── src/
│ │ ├── main.rs # CLI entry point (clap); daemon run owns main thread for tray
│ │ ├── commands/ # Command handlers: status, auth, repos, views, config, doctor, daemon
│ │ │ ├── daemon.rs # pulsos daemon [run|start|stop|status]
│ │ │ ├── wizard.rs # First-run config wizard (auth + discovery + re-check)
│ │ │ └── ui/ # Full-screen interactive prompt layer (ScreenSession)
│ │ ├── daemon/ # Background daemon subsystem
│ │ │ ├── engine.rs # Wraps run_poller; broadcasts DataSnapshot via SSE channel
│ │ │ ├── server.rs # Axum SSE server (/api/stream, /health); writes daemon.port
│ │ │ ├── tray.rs # Native tray icon (macOS/Windows only; tray-icon + tao + muda)
│ │ │ └── notify.rs # Desktop notifications on endpoint state transitions
│ │ ├── tui/ # ratatui live TUI (5 tabs: Unified, Platform, Health, Settings, Logs)
│ │ │ ├── poll.rs # Background poller; connects to SSE if daemon is running
│ │ │ ├── actions.rs # ActionRequest/ActionResult async channel for settings
│ │ │ └── widgets/ # Per-tab rendering components + log_buffer, settings_flow
│ │ └── output/ # Table, compact, JSON formatters
│ └── pulsos-test/ # Test helpers and builders (EventBuilder, mock servers)
└── Cargo.toml # Workspace root
| File | Path | Notes |
|---|---|---|
| Port file | ~/.config/pulsos/daemon.port |
Written by SSE server on bind; mode 0600 on Unix |
| PID file | ~/.config/pulsos/daemon.pid |
Written by daemon run on startup |
| Token file | ~/.config/pulsos/daemon.token |
64-char random hex bearer token; mode 0600 on Unix; removed on stop |
| Credentials | ~/.config/pulsos/credentials.toml |
File-based fallback; mode 0600 on Unix |
| Config | ~/.config/pulsos/config.toml |
Main TOML config |
| Platform | Interval | Notes |
|---|---|---|
| GitHub | 30s (adaptive) | Backs off to 60s/120s as rate limit is consumed |
| Railway | 15s | |
| Vercel | 15s | |
| Health checks | 30s | Per-platform token + connectivity readiness |
| Container metrics | 30s | Railway CPU/RAM via GraphQL |
| Endpoint pings | 8s | Blackbox TTFB/uptime; does not consume platform quota |
- Rust 1.75+
cargo build --workspacecargo test --workspacecargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warningscargo run --bin pulsos -- status
cargo run --bin pulsos -- status --watch
cargo run --bin pulsos -- doctor
cargo run --bin pulsos -- daemon runMIT
Last Updated: 2026-02-23
