Skip to content

Pulse — Slack Status Sync

Pulse is a self-contained module in the Nucleus monorepo at apps/pulse/. It’s a macOS menu bar app plus its own Cloudflare Worker and D1 database. The goal is simple: a team member’s Slack status reflects what they’re actually doing right now — Spotify when idle, active work (Claude Code running, git commits, Figma frontmost, Productive activity, GitHub pushes) when working. The app rotates through the top signals every few seconds so colleagues see more than one thing.

Shipped as an internal DotCollective tool first. Architecture stays lift-out-able so it could go public or open-source without rewriting.

  • Worker: https://pulse.nucleus.fast (Nucleus Cloudflare account)
  • D1 database: pulse-db (own database, no shared tables with Nucleus app)
  • Mac app: ~/Applications/Pulse.app, signed with a local Pulse Dev Signing self-signed certificate (Apple Developer ID cert replaces this when Pulse is ready to distribute beyond DotCollective)
  • macOS requirement: 14+
┌──────────────────────────────────────────┐
│ Pulse Mac app (Swift / SwiftUI) │
│ ├─ Watchers (in-process actors) │
│ │ ├─ Spotify (AppleScript, 5s) │
│ │ ├─ Claude Code (FSEvents) │
│ │ ├─ Figma (AXUIElement, 3s) │
│ │ ├─ Git hook (NWListener 8811) │
│ │ ├─ GitHub (REST, 2 min) │
│ │ └─ Productive (Worker proxy, │
│ │ 60s) │
│ ├─ StatusBus + priority-queue rotator │
│ └─ Slack driver (set via Worker proxy) │
└──────────┬───────────────────────────────┘
│ HTTPS (dev bearer or device token)
┌──────────────────────────────────────────┐
│ pulse.nucleus.fast (Hono) │
│ ├─ /config GET/PUT │
│ ├─ /oauth/slack + /callback │
│ ├─ /slack/status (users.profile.set│
│ │ proxy) │
│ ├─ /productive/token │
│ ├─ /productive/status + /activities │
│ └─ /auth/device/{pair,verify} │
└──────────┬───────────────────────────────┘
┌──────────────────────────────────────────┐
│ Pulse D1 (devices, configs, tokens, │
│ events, pair_codes) │
└──────────────────────────────────────────┘
  • Clean boundary: Pulse isn’t a Nucleus feature — it’s a companion tool with a different lifecycle and risk profile.
  • Independent deploy: breaking Pulse can’t take down Nucleus.
  • Separate secrets: Slack / Productive OAuth creds live in Pulse’s wrangler config, not Nucleus’s.
  • Portability: moving Pulse into its own repo later is a single folder copy — no untangling from the Nucleus Worker or D1.
  • Tiny footprint: a few MB of D1 usage and a Worker that runs well inside the free tier.

The only monorepo benefit Pulse takes is shared tsconfig, ESLint, Prettier, and the same Wrangler CLI version.

Each source runs as its own in-process actor on the Mac. Events are written to a central StatusBus. The rotator reads from the bus, picks the top 3 by priority, rotates through them, and proxies the result to Slack via the Worker.

SourceMechanismPriorityTTLFormat
Git commitsLocal post-commit hook → NWListener :88119030sCommitted to {repo}/{branch}
GitHub pushREST /users/{u}/events every 2 min855 minPushed to {repo}
Claude CodeFSEvents on ~/.claude/projects8060s debounceClaude working on {repo}/{sub}
FigmaAXUIElement focused window title (3s poll)702 min focus memoryDesigning in {file}
ProductiveWorker proxy /productive/activities (60s poll)602 minCreating/Working on/Cleaning up {item}
Google CalendarWorker proxy /google-calendar/current-meeting (60s poll) + local camera/mic KVO40event end timeIn meeting: {title}
SpotifyAppleScript (5s poll)10none{track} — {artist}

When the Mac app saves GitHub settings it runs GitHookInstaller.install() which writes a single hook to ~/.git-hooks/post-commit and points git config --global core.hooksPath at it. The hook calls entire hooks git post-commit (no-op when Entire CLI isn’t installed) then curls http://127.0.0.1:8811/git-commit with {repo, branch, sha, message}. Repo name is resolved via git rev-parse --git-common-dir so worktrees show the main repo name instead of the worktree directory.

Google Calendar — why camera/mic gate the status

Section titled “Google Calendar — why camera/mic gate the status”

A meeting event alone isn’t enough: people skip meetings. Pulse requires both a live calendar event and active camera or microphone usage before it posts “In meeting” to Slack. The calendar side is the Worker calling events.list on the primary calendar with a small window around now (skipping all-day blocks, declined events, and events with no conferencing). The presence side is a Swift CameraMicMonitor that KVO-observes AVCaptureDevice.isInUseByAnotherApplication on every camera and microphone the system enumerates — it never opens a capture session, only reads the flag. Camera/mic changes immediately re-evaluate the watcher so the status appears within a second of joining a Meet / Zoom / Teams call.

Google access tokens expire in 1h, so the Worker transparently refreshes using the stored refresh_token when the stored access token is within 60s of expiry. The initial OAuth request uses access_type=offline with prompt=consent to guarantee a refresh token on first connect.

The watcher also publishes to a MeetingPresence observable on the Mac side — a deliberate seam for a future Deepgram-based meeting recorder to subscribe to without reworking the detector.

Productive — how the activity filter works

Section titled “Productive — how the activity filter works”

Productive personal API tokens use X-Auth-Token, not Authorization: Bearer (that’s OAuth only). The watcher queries /api/v2/activities?filter[creator_id]={personId} — the creator_id filter returns activities where the user was the actor. (Filtering by person_id returns activities where the user is the subject — profile updates, salary changes — not what you’d expect.) The personId itself is looked up at connect time by matching the authenticated user’s email against /api/v2/people, preferring exact email matches and falling back to the first record with a first_name/last_name. Activities come back in arbitrary order, so the Worker over-fetches 200 and client-side sorts by created_at desc.

The Worker’s auth middleware accepts either:

  1. Device token (the v1 path) — Authorization: Bearer <deviceToken>. Issued by POST /auth/device/verify after the user trades a 6-digit code issued by POST /auth/device/pair. Token is stored hashed (SHA-256) in D1’s devices table; the client keeps the cleartext in macOS Keychain. Pair codes live 10 minutes. The code is delivered via Resend when RESEND_API_KEY is set; otherwise the Worker returns the code inline for solo-developer bootstrap.
  2. Dev bearer fallback — Authorization: Bearer <DEV_BEARER_TOKEN> plus an X-Pulse-Dev-Email: you@example.com header. Used for local dev and for Pulse’s internal rollout before every developer has paired. Both paths work simultaneously.

Provider tokens (Slack user tokens, Productive personal tokens) are encrypted with AES-GCM using TOKEN_ENCRYPTION_KEY (a base64 32-byte Worker secret) and stored in the tokens table. The Worker never returns a provider token to the client — it proxies the API calls that need them.

apps/pulse/mac/Sources/Pulse/
├─ PulseApp.swift @main, MenuBarExtra + Settings + Pair windows
├─ AppDelegate.swift forces .accessory policy (no dock)
├─ MenuBarContent.swift popover UI (header icons, slack set-status,
│ now-playing, rotator + source toggles, footer)
├─ PulseStore.swift @MainActor ObservableObject — owns watchers,
│ rotator, credentials, bus subscription
├─ PulseAPI.swift Worker HTTP client (actor)
├─ PulseConfig.swift Codable types ([String: T]-keyed to round-trip
│ cleanly with the Worker JSON)
├─ StatusEvent.swift source event payload
├─ StatusBus.swift @MainActor sink for watcher events
├─ Rotator.swift top-3 priority, rotation, 4s Slack throttle
├─ SpotifyWatcher.swift NSAppleScript 5s poll
├─ ClaudeWatcher.swift FSEvents + cwd parse + worktree-aware repo name
├─ GitHookListener.swift NWListener 127.0.0.1:8811
├─ FigmaWatcher.swift AXUIElement 3s poll, 2-min focus memory
├─ GitHubWatcher.swift REST poll, dedupe by push.id
├─ ProductiveWatcher.swift Worker proxy poll, 60s
├─ GoogleCalendarWatcher.swift Worker proxy poll, 60s, gated by camera/mic
├─ CameraMicMonitor.swift AVCaptureDevice KVO, no capture session
├─ MeetingPresence.swift observable "in meeting now" for future Deepgram hook
├─ CurrentMeeting.swift Codable mirror of shared/schemas CurrentMeeting
├─ GitHookInstaller.swift writes ~/.git-hooks/post-commit + core.hooksPath
├─ SlackEmoji.swift ~450 emojis across 8 categories
├─ EmojiPickerButton.swift searchable, category-tabbed picker
├─ Keychain.swift SecItem wrapper (uses SecItemUpdate to avoid
│ re-triggering the Always-Allow prompt)
└─ PulseApp.swift shared window scene + IDs

D1 tables live in apps/pulse/worker/migrations/.

devices (id, email, device_token_hash, name, created_at, last_seen_at)
configs (email PK, sources_enabled, priority_overrides,
rotation_interval_s, format_templates, paused_until, updated_at)
tokens (email, provider PK, encrypted_token, scope, expires_at, metadata)
events (id, email, source, status_text, status_emoji, priority,
created_at) -- indexed by (email, created_at DESC)
pair_codes (email, device_id PK, code_hash, expires_at)
Terminal window
cd apps/pulse/worker
pnpm typecheck
npx wrangler deploy # pushes to pulse.nucleus.fast
npx wrangler d1 migrations apply pulse-db --remote # when migrations change

Secrets (all set via npx wrangler secret put): DEV_BEARER_TOKEN, TOKEN_ENCRYPTION_KEY, SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, PRODUCTIVE_ORG_ID (currently 1476).

Terminal window
cd apps/pulse/mac
swift build -c release
APP=~/Applications/Pulse.app
mkdir -p "$APP/Contents/MacOS"
cp .build/release/Pulse "$APP/Contents/MacOS/Pulse"
cp Info.plist "$APP/Contents/Info.plist"
codesign --force --sign "Pulse Dev Signing" "$APP"
open "$APP"

The Pulse Dev Signing identity is a user-level self-signed cert sitting in the login keychain (created once via openssl + security import + security add-trusted-cert -r trustRoot -p codeSign). Signing with the same identity every build means the Keychain’s “Always Allow” ACL persists across rebuilds — no more permission prompt every time you ship a new binary.

  • VS Code / Cursor detection
  • Multi-workspace Slack
  • Custom emoji per track / file
  • Web-based settings UI
  • Team admin view
  • Sparkle auto-updates
  • Non-macOS clients
  • Nucleus Access SSO integration (Pulse has its own auth)

The full brief that drove the build lives at docs/pulse-mac-app.md.