Self-Hosting Quickstart
Happy path: pulse configure → pulse init → pulse run.
Install
Ships pulse and pulse-mcp.
One-liner (installs pipx if needed, then pulse-agent; prints CLI-matched styling; runs pulse onboard at the end when a real terminal is available - including curl | bash, by attaching stdin to /dev/tty when needed):
curl -fsSL https://pulseagent.dev/install.sh | bashSkip the onboarding wizard at the end:
curl -fsSL https://pulseagent.dev/install.sh | bash -s -- --no-onboardManual install (if you already use pipx):
pipx install pulse-agentOther methods
- uv -
uv tool install pulse-agent - pip - use a virtualenv;
pip install pulse-agent
Docker
The image serves the app on port 8000, keeps config under /config (PULSE_CONFIG_DIR), and SQLite plus the vault under /data. The Dockerfile builds the pulse_agent wheel inside the image (no local uv build required). On tagged releases, CI publishes to ghcr.io/<github-owner>/<repo> (GitHub lowercases the path; substitute your fork’s owner and repository name).
Pull from GitHub Container Registry
Example for this repository (adjust owner/repo for your fork):
docker pull ghcr.io/jeff7712/pulse:3.1.1If the package is private, authenticate before pull:
echo YOUR_GITHUB_PAT | docker login ghcr.io -u YOUR_GITHUB_USERNAME --password-stdinUse a personal access token with the read:packages scope (classic PAT) or fine-grained access to read GitHub Packages.
Run the server
Typical detached run:
docker run -d --name pulse \
-p 8000:8000 \
-v pulse-config:/config \
-v pulse-data:/data \
ghcr.io/jeff7712/pulse:3.1.1Open http://localhost:8000. For logs in the foreground, omit -d (add --rm if you do not need a named container).
If you built locally with -t pulse, replace the image name with pulse.
First-time setup (interactive)
pulse configure and pulse onboard need a TTY. Full first-run wizard and then start the server in the same container:
docker run --rm -it \
-p 8000:8000 \
-v pulse-config:/config \
-v pulse-data:/data \
ghcr.io/jeff7712/pulse:3.1.1 \
pulse onboardConfigure only, then run a separate long-lived docker run (as above) for pulse run:
docker run --rm -it \
-v pulse-config:/config \
-v pulse-data:/data \
ghcr.io/jeff7712/pulse:3.1.1 \
pulse configureBuild and run from a clone
From the repository root:
docker build -t pulse -f Dockerfile .
docker run -d --name pulse \
-p 8000:8000 \
-v pulse-config:/config \
-v pulse-data:/data \
pulseDocker Compose
From the repository root:
docker compose up --build -dUses root compose.yaml; same URL http://localhost:8000. The service uses restart: unless-stopped so the container comes back after reboot (until you docker compose down).
systemd (native pulse install)
For a pipx / uv tool install layout, copy and enable the example user unit from deploy/systemd/pulse-user.service.example to ~/.config/systemd/user/pulse.service, adjust ExecStart if your pulse binary lives elsewhere, then systemctl --user daemon-reload and systemctl --user enable --now pulse.service. Use loginctl enable-linger "$USER" if you want the unit to start at boot without an interactive session.
NixOS module
The flake exposes nixosModules.default, a systemd service that runs pulse run. In your system flake:
{
inputs.pulse.url = "github:JEFF7712/pulse";
outputs = { nixpkgs, pulse, ... }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
modules = [
pulse.nixosModules.default
{ services.pulse.enable = true; } # optional: services.pulse.port, .stateDir, .host, .environment
];
};
};
}Before starting, place your pulse.toml (with secrets) in services.pulse.stateDir (default /var/lib/pulse), or run pulse configure against it - secrets are never put in the Nix store. The service binds 127.0.0.1:8000 by default and reads PULSE_CONFIG_DIR / PULSE_DATABASE_PATH / PULSE_VAULT_PATH from that directory.
Data volumes and stopping
pulse-data- database and vault (/datain the container).pulse-config- configuration (/configin the container).
Stop and remove the container:
docker stop pulse && docker rm pulseWith Compose: docker compose down. Named volumes persist until you remove them explicitly, for example:
docker volume rm pulse-config pulse-dataThat deletes stored config and data - use only when you intend to reset.
Any Python 3.12+ base image - install pulse-agent with pip, set the same PULSE_* paths (or bind-mount host directories to match), then pulse run --host 0.0.0.0 --port 8000.
Check with pulse --help after any install path.
The install script lives at scripts/install.sh in the repository if you prefer to download and audit it before running.
Developer install
- uv:
uv sync(optional--group dev) - Nix:
nix develop, thenuv sync --group dev - venv:
python3 -m venv .venv→ activate →pip install -e .
Run the FastAPI app from a clone
uv run uvicorn --app-dir src pulse.app.main:create_app --factory1. Configure
If you use Google, Spotify, GitHub, or Plaid, create OAuth (and Plaid) apps first so client IDs and secrets are ready. Oura: personal access token or OAuth app.
pulse configureMenu: Core (database, vault, timezone), Connectors (per-source creds + OAuth/Plaid/Oura when ●), Notifications, Full wizard. TTY: arrows + Enter; else digits. PULSE_* overrides top-level TOML when set in the environment.
Config file: Prefer .config/pulse.toml or repo-root pulse.toml. Override with PULSE_CONFIG_FILE or PULSE_CONFIG_DIR. Start from pulse.toml.example; connectors default to disabled until you enable them.
Use pulse auth to see what is authorized and what still needs credentials, and pulse auth <source> (google, github, spotify, plaid, oura; gmail/calendar/youtube all resolve to google) to run one flow directly. It checks prerequisites and prints the exact redirect URI to register before opening a browser. The same flows also run from Configure → Connectors (loopback callbacks on 8888, 8891, 8893, 8894 as applicable; Spotify's redirect URI must be registered as http://127.0.0.1:8888/callback, since Spotify rejects the localhost hostname as insecure). Complete this before pulse init if the first pull should hit those APIs. Browser history skips browser OAuth here.
Shortcut: pulse onboard walks through the same configure areas as [cmd]pulse configure[/] (core → connectors → notifications), then runs connector OAuth / Plaid / Oura when credentials and enabled connectors allow it. Use pulse onboard --strict to fail if any auth step fails. Profile flags match pulse init (-f, --profile-text); server: --host, --port, --log-level.
2. pulse init
pulse initEnsures vault README.md and Meta/AGENTS.md exist (created once if missing), writes 04-Config/profile.md, runs initial pulls and day aggregation.
Interactive TTY profile step: copy the plain-text export prompt (between rules, no box borders), paste the assistant’s reply, then type ---END--- on its own line and press Enter (or finish with Ctrl-D / Ctrl-Z+Enter). Use -f / --profile-text to skip prompts.
3. pulse run
pulse runServes on 0.0.0.0:8000 by default (--host / --port / --log-level to change). / - operator page with Pull and Test Telegram (same pipelines as CLI where noted).
4. Inspect
pulse statusstatus - DB path, event counts, cursors.
| Command | Purpose |
|---|---|
pulse pull [sources…] | Immediate connector pulls |
pulse discover [--date …] | Manual day aggregation |
pulse auth [source] | Authorization status, or run one OAuth/Link flow |
pulse review | Force a discovery pass now (if configured) |
pulse test-telegram | One-off Telegram test |
Re-open Configure → Connectors anytime to re-auth or edit pulse.toml.
Pattern discovery (optional)
Enable [discovery] in pulse.toml (see pulse.toml.example). Ensure your agent CLI is on PATH and a notification channel is configured.
Once enabled, pulse run invokes your agent every interval_days at at (config timezone) to look for long-horizon structure: how the mix of what you do has drifted over months, whether your interests rotate rather than accumulate, sleep-phase drift, what holds your attention versus what you only glance at, and what quietly stopped.
It is deliberately not a report on your week - you already remember your week, so reading it back is worthless. You are notified only when the agent records a genuinely new pattern in the vault, which on real data is closer to monthly than weekly. Force a pass with pulse review. Each run spends your agent subscription.
Connect Pulse to your coding agent (MCP)
Use the Model Context Protocol so Claude Code, OpenClaw, Cursor, and other MCP clients can call Pulse tools (pulse_query_events, pulse_digest, pulse_longitudinal_profile, pulse_change_surface, pulse_coverage, the pulse_pattern_* and pulse_vault_* tools, …) against the same database and vault as this install.
Send your coding agent - copy everything in the box into the agent chat (it will fetch the doc and do the work):
Read https://pulseagent.dev/docs/self-hosting/mcp-agent-setup.html and follow every step to install Pulse (pulse-agent), ensure pulse.toml exists, and register pulse-mcp in my MCP settings for this machine.Vault (Obsidian)
Output lives under vault_path / PULSE_VAULT_PATH. Common patterns: dedicated folder as its own vault; subfolder inside an existing vault; or symlink (mobile/sync may not handle symlinks well). First vault use may create README.md (structure + reserved headings) and Meta/AGENTS.md.