Data sources at a glance
Manual, Connector, HTTP and Bash compared: what each needs, when it runs, how the test run works, and why a changed command has to be approved again.
Every KPI in Pulsaria has exactly one data source. It decides who produces the number: you, a ready-made connector, an HTTP endpoint, or a shell command on your Mac. This page compares the four, explains the test run and the approval model, and shows where a failed fetch surfaces.
The four sources side by side
| Manual | Connector | HTTP | Bash | |
|---|---|---|---|---|
| What you provide | the number itself | a metric from the Connector Library plus a connection | a URL, optional headers, a JSONPath | a shell command that prints one number |
| Setup before the first run | none | a credential from the external service and one or two parameters, entered in a 3-step wizard | the endpoint and the path into its JSON | the command |
| Needs your approval | no | no | yes | yes |
| Where the secret lives | — | macOS Keychain | plain text in kpi-definitions.json | plain text inside the command |
| When it runs | when you type a value | on its cron schedule, or on demand | on its cron schedule, or on demand | on its cron schedule, or on demand |
| Platform | any | automatic fetching macOS only | automatic fetching macOS only | macOS only, always |
Automatic fetching happens while Pulsaria is open, on macOS. There is no background agent and no menu-bar helper: quit the app and nothing is fetched. On any other platform the scheduler’s fetch tick returns immediately — that includes Connector and HTTP KPIs, not just Bash — and the Refresh and Run now buttons are not rendered at all.
Manual
You type the value in yourself, either on the Daily Entry screen or from a KPI’s detail page with Add value. There is no schedule field on a manual KPI and nothing can fail. This is the right choice for numbers no API has: a sales-call count, a subjective quality score, a figure you read off someone else’s dashboard once a week.
Details: Entering values.
Connector
Six connectors ship with the app — Google Search Console, Google Analytics 4, Plausible, Polar, Stripe and Awin — with 33 metrics between them. You pick a metric from the Connector Library, then a connection: a credential plus the parameters that service needs (a site URL, a property ID, a publisher ID). The credential goes into the macOS Keychain, never into Pulsaria’s data files, and one connection can feed as many KPIs as you like.
The metric also pre-fills the KPI’s unit, direction, KPI type, period and cron schedule, so a connector KPI is usually three clicks and one paste. Connector KPIs need no approval: the request template is compiled into the app, with fixed hosts and encoded parameters.
Details: Connectors.
HTTP
For any JSON API that no connector covers. You give a URL, optional header rows, and a dot/bracket path into the response such as data.items[0].value. Pulsaria sends a GET request from its native layer, parses the JSON and stores whatever number the path resolves to.
A token typed into a header row is stored in plain text in
kpi-definitions.json— and therefore also lands in a JSON export. Only connector credentials go to the Keychain.
Details: Custom HTTP sources.
Bash
Any command that writes a single number to stdout. It runs through /bin/sh -c with a deliberately minimal environment (only PATH, HOME and LANG are passed through — your shell profile is not loaded) and is killed after 10 seconds. The command runs with your normal user permissions; there is no sandbox.
Details: Shell commands.
Creating a KPI starts with the source
The New KPI dialog leads with the decision that shapes everything else. It opens on a picker headed “Where does this KPI’s data come from? You can change this later.” with four cards: Manual, Connector, Bash, HTTP.
- Dashboard → + KPI in a group header (or Create KPI on a fresh install).
- Pick a source card.
- The form opens with the Data source section first and the Goal section below it.
Picking Connector opens the Connector Library straight away, because the metric you choose there fills in half the form. Choose a connector on the left, a metric card on the right, then Use selected metric.
The order is not cosmetic. The source is what defines what is measured, and a connector metric overwrites the goal fields below it — so the form reads top to bottom without anything changing behind your back.
When you edit an existing KPI the picker is skipped. The dialog opens on the form, and the source becomes a four-way switch you can flip in place.
The test run
Every automatic source can be run once without storing anything. In the KPI dialog, press Run test (the label switches to Running…). A test run writes no KPI value, no fetch-log row and no alert — it only shows you what would happen.
The same panel appears in two more places: on step 3 of the connection setup wizard, and behind the circular-arrow Test button on a row in App Settings → Connections.
Changing the command, URL, headers or JSONPath clears a previous result, so what you see on screen always belongs to what is currently in the fields.
What the result panel shows
The header reads Parsed value with the number in green (Preview: 1234), or Test failed in red. Below it, only the sections that have data:
| Section | Contents |
|---|---|
| Command / URL | the exact invocation after date tokens were resolved, with a Copy button |
| Output / Response body | stdout for Bash, the response body otherwise; collapsed to six lines with Show more once it exceeds 400 characters or 6 lines |
| Error | stderr (Bash only) and the error message (any source) |
| meta row | Shell and Exit code for Bash, Method and HTTP status otherwise — plus Duration in milliseconds |
The number in the preview is produced by the same code the scheduler uses, so a green test means a scheduled fetch would store exactly that value.
Two rough edges worth knowing: response bodies are cut to 4,096 characters before they are shown, and for a connector test inside the KPI dialog the meta row always prints POST as the method, even for the connectors that actually use GET.
The Bash test refuses on anything but macOS with “Shell commands are only available on macOS”. The HTTP test runs everywhere, but the resulting KPI still will not fetch on a schedule off macOS.
Approval: why Bash and HTTP need your explicit OK
Bash and HTTP KPIs are written by you and can point anywhere. Pulsaria therefore refuses to run one until you have confirmed the exact configuration, and it keeps a fingerprint of what you confirmed.
While a Bash or HTTP KPI is unapproved, an amber Approval required banner sits at the top of the dialog: “Auto KPIs only run after you approve them. Run a test below, then click Approve. Without approval the scheduler will skip this KPI.” Press Approve, confirm in the dialog that follows, and a green Approved banner replaces it.
Under the hood the app stores a SHA-256 fingerprint of exactly what you saw:
- for Bash, the command text as written;
- for HTTP, the URL, the headers with their keys sorted, and the JSONPath — so reordering header rows does not invalidate anything.
Before every fetch — scheduled or manual — the fingerprint is recomputed and compared:
| State | What happens |
|---|---|
| fingerprint matches | the KPI runs |
| no fingerprint stored | the run is skipped, logged as “Command not approved — please confirm in KPI settings” |
| fingerprint no longer matches | the run fails with “Command changed — please re-approve” |
The point is that kpi-definitions.json is a plain file on your disk. Another program, a sync conflict from a second Mac or a hand edit could change a command after you approved it. The fingerprint means Pulsaria never executes a command you have not seen.
You can save an unapproved KPI — it just will not fetch. Saving one raises the toast Saved but not approved / “The scheduler will skip this KPI until you approve it.”
What invalidates an approval
- Editing the command, URL, headers or JSONPath.
- Duplicating the KPI. The copy starts unapproved and the toast says so.
- Importing a KPI from a JSON export. Approvals are stripped from every imported KPI on purpose, so nothing arrives pre-authorised from another machine.
Date tokens are the deliberate exception. {{today}}, {{firstDayOfMonth}} and the other 13 tokens are stored as written and only resolved at the moment of the fetch, so a rolling query stays approved while its dates keep moving.
Connector KPIs skip this whole flow: no banner, no Approve button, no fingerprint.
What the confirmation dialog shows
Pressing Approve opens Confirm Bash command or Confirm HTTP configuration, showing the command — or the URL and JSONPath on two lines — one more time. For Bash, the text is scanned for ten obviously dangerous patterns (rm -rf, sudo, dd if=, mkfs, a fork bomb, curl … | sh, wget … | sh, eval, chmod 777, writing to /dev/sd*) and any hit is listed under “Suspicious patterns detected:” in plain language. The footer reads “Only approve sources you trust. Later edits require re-approval.”
That scan is a hint, not a security boundary. An approved command runs with your full user permissions, and the Approve button does not require a test run — you can approve something you never ran, or something that just failed.
When a fetch fails
A failed fetch never writes a value. The KPI card keeps its last good number and its status is unchanged, which is why a silently broken source can look fine on the Dashboard for a while.
There is no retry and no backoff. The next scheduled firing is the retry.
Where failures show up
- A toast, once. A scheduled failure raises a red Auto-KPI failed toast reading
<KPI name>: <error>with a View details action that opens the Fetch Log. - The batch summary. Refresh on the Dashboard reports
3 successful · 1 failed · 2 skipped. With something skipped the toast is informational (Auto KPIs updated) and offers an Approve shortcut to the first skipped KPI; with only errors it is red (Auto KPIs had errors) and offers Details. With no automatic KPIs at all it reads No auto KPIs configured. - Per KPI. Run now on a card or on the detail page reports KPI updated with
<name>: <value>, or Auto-KPI failed with View details. - The Fetch Log. Every attempt — success, failure and skip — is written to
/logs, newest first, capped at 500 entries. This is the only place a failure survives after the toast disappears. There is no email and no push notification.
Skipped runs are stored as unsuccessful, so on the Fetch Log page they sit under the Errors filter with the same red icon as real failures. The amber “skipped” treatment only exists in the refresh summary.
Messages you are likely to see
| Message | What it means |
|---|---|
Command not approved — please confirm in KPI settings | the KPI was never approved; it was skipped, not attempted |
Command changed — please re-approve | the stored command no longer matches the fingerprint |
Output is not a number | the command succeeded but stdout was empty or not numeric |
Exit code 1 | the command exited non-zero; stderr is in the log row |
Command timed out after 10s | the shell command hit the hard limit |
HTTP 401 | a custom HTTP KPI got a non-2xx response |
JSONPath result not numeric | the path missed, or what it found is not a number |
Credentials missing from keychain. Please re-authorize the connection. | the Keychain entry for that connection is gone |
Connection not found (deleted?) | the connection this KPI points at was deleted |
Connector failures get translated, human-readable messages — “Access denied (403). Make sure the Service Account email is granted access to the property.” and similar. Custom HTTP KPIs do not: they get the bare HTTP <status>.
Shared limits
These apply to every automatic source and cannot be configured:
- 10-second timeout, for shell commands and HTTP requests alike.
- 64 KB response cap. A larger body is truncated, which usually breaks JSON parsing.
- Only
http://andhttps://, at most 5 redirects, and redirects to any other scheme are refused. - Custom HTTP KPIs are GET only. There is no method picker and no request body.
- The fetched number is stored under today’s local calendar date, whatever period the number itself describes.
- While the trial has expired or a licence was revoked, automatic fetching is paused entirely and Refresh is disabled with the tooltip “Requires a license or active trial”.
Where to go next
- Connectors — the six services, their metrics, and how connections work.
- Custom HTTP sources — URL, headers, JSONPath and date tokens.
- Shell commands — the execution environment and what breaks in it.
- Schedules & refresh — cron presets, catch-up after the app was closed, and the manual triggers.
- Fetch log — reading the record of every attempt.
- Privacy: what leaves your Mac — which hosts an automatic KPI talks to.