rust · tui · mit

one window for all your local dev servers

mprocs spawns commands in panes. pulse does that plus http probes, port watch, live traffic tap, and restart cascades on depends_on. all from a single pulse.toml.

pulse — your dev stack
$ pulse
╭── pulse · booting · 0 / 5 ready ───────────────────────────╮
    postgres  00:00:01  starting             :5432 waiting 
         Ծ     robot   : "PROC_INIT: postgres"              
                                                            
    redis     00:00:00  waiting              --            
         -_-   cat     : "wake me when the db is up"        
                                                            
    api       00:00:00  waiting              --            
         ...   goblin  : "can i eat yet"                    
                                                            
    web       00:00:00  waiting              --            
         zZz   blob    : "will stir when asked"             
╰────────────────────────────────────────────────────────────╯
  j/k  nav    r  restart    s  stop    t  tap    ?  help
$ pulse
╭── pulse · booting · 2 / 5 ready ───────────────────────────╮
    postgres  00:00:08  ready                :5432 bound   
          robot   : "PROC_ACTIVE: postgres"            
                                                            
    redis     00:00:06  ready                :6379 bound   
         =^.^= cat     : "acceptable"                       
                                                            
    api       00:00:02  probing...           :4000 waiting 
         @_@   goblin  : "tickling port 4000"               
                                                            
    web       00:00:00  waiting              --            
         zZz   blob    : "it is not time"                   
╰────────────────────────────────────────────────────────────╯
  j/k  nav    r  restart    s  stop    t  tap    ?  help
$ pulse
╭── pulse · stack healthy · 4 / 5 ready ─────────────────────╮
    postgres  00:01:14  ready                :5432 bound   
          robot   : "SYSTEMS NOMINAL"                  
                                                            
    redis     00:01:12  ready                :6379 bound   
         =^.^= cat     : "purring along"                    
                                                            
    api       00:01:06  200 · 14ms · 100%    :4000 bound   
          goblin  : "fed. happy. mostly"               
                                                            
    web       00:01:03  200 · 38ms ·  99%    :3000 bound   
         ( ᴗ ) blob    : "great day for shapes"             
╰────────────────────────────────────────────────────────────╯
  j/k  nav    r  restart    s  stop    t  tap    ?  help
$ pulse
╭── pulse · stack degraded · redis down ─────────────────────╮
    postgres  00:01:23  ready                :5432 bound   
          robot   : "SYSTEMS NOMINAL"                  
                                                            
    redis     00:01:21  exit 139             :6379 freed   
         ✗_✗   cat     : "knocked redis off the counter"    
                                                            
    api       00:01:15  502 · 1.8s ·  64%    :4000 bound   
         @_@   goblin  : "api: ECONNREFUSED redis:6379"     
                                                            
    web       00:01:12  500 · 2.1s ·  61%    :3000 bound   
         (-_-) blob    : "shapes trembling"                 
╰────────────────────────────────────────────────────────────╯
  j/k  nav    r  restart    s  stop    t  tap    ?  help
$ pulse
╭── pulse · restart cascade · depends_on ────────────────────╮
    postgres  00:01:28  ready                :5432 bound   
          robot   : "awaiting cluster sync"            
                                                            
    redis     00:00:02  starting             :6379 claimed 
         =o.o= cat     : "i did not do that"                
                                                            
    api       00:00:01  restarting           :4000 waiting 
         Ծ     goblin  : "cascade: redis → api"             
                                                            
    web       00:00:01  restarting           :3000 waiting 
         Ծ.Ծ   blob    : "re-assembling shape"              
╰────────────────────────────────────────────────────────────╯
  j/k  nav    r  restart    s  stop    t  tap    ?  help
$ pulse
╭── pulse · stack healthy · recovered ───────────────────────╮
    postgres  00:01:42  ready                :5432 bound   
          robot   : "CLUSTER SYNCED"                   
                                                            
    redis     00:00:14  ready                :6379 bound   
         =^.^= cat     : "back on the counter"              
                                                            
    api       00:00:12  200 · 12ms · 100%    :4000 bound   
          goblin  : "third restart. charmed"           
                                                            
    web       00:00:11  200 · 31ms ·  98%    :3000 bound   
         ( ᴗ ) blob    : "shape stable"                     
╰────────────────────────────────────────────────────────────╯
  j/k  nav    r  restart    s  stop    t  tap    ?  help

drop a pulse.toml in your repo root, run pulse, get a terminal ui where every service has its own log pane, its own health status, and its own tiny ascii sentinel complaining when things die. edit the config while it's running, the watcher diffs it and respawns what changed.

01

http probes

per-service poll with latency + rolling 60-sample success rate in the sidebar. status code badge flips red the moment the endpoint stops behaving.

02

port watch

[service.port] expect = 3000 polls a tcp connect every couple seconds. badge reads :3000 bound (cyan) or :3000 free (dim).

03

traffic tap

tiny reverse-proxy in front of your service records the last 500 requests. press t for the list, T to split on a specific one and see headers + body preview.

04

dep restart cascade

depends_on = ["postgres"] and web will bounce on its own when postgres recovers. 1s grace, configurable. no more "restart everything" scripts.

05

ascii sentinels

one of five species per service — goblin, cat, ghost, robot, blob. they change face on state transitions and drop short lines in the status bar when something interesting happens.

06

auto-discover

pulse init reads package.json scripts, docker-compose.yml services, and Procfile entries. drafts a config you can run in ~10 seconds.

a word on the sentinels

three of five species. each has its own voice and its own panic behaviour.

ᨀ   goblin   "api back up. third time's the charm"
≋   blob     "postgres is just vibing at 5432"
ᓚ   cat      "probe latency is weird, i'm watching it"

robot and ghost round out the set. robot is pure ascii for fonts that don't like the other glyphs. i spent more time on the message pools than i should have.

pulse vs the usual suspects

feature                        pulse   mprocs   tmux    pm2
─────────────────────────────  ─────   ──────   ────    ───
multiplex processes             yes     yes      yes     yes
per-service log panes           yes     yes      diy     yes
http health probe               yes     no       no      diy
port-in-use detection           yes     no       no      no
restart cascade on deps         yes     no       no      no
live traffic tap + replay       yes     no       no      no
ascii sentinel per service      yes     no       no      no
single toml config              yes     yes      no      yes
auto-discover from repo         yes     no       no      no

pm2 is production-shaped and it shows in local dev. tmux is a multiplexer, not a process manager. mprocs is still the thing to beat. pulse is the direction i wanted mprocs to go.

install

one-liner
curl -fsSL https://pulse.frkhd.com/install.sh | sh

detects your os + arch, drops the binary on your PATH. set PULSE_INSTALL_DIR if you want a specific spot.

homebrew
brew install f4rkh4d/tap/pulse

macos + linuxbrew. pinned to the latest release.

from source
cargo install --git https://github.com/f4rkh4d/pulse

rust 1.75+. grabs the latest main. slowest of the three, but you pick the commit.

mac (apple silicon + intel) and linux (amd64 + arm64). windows is on the list but not here yet.