A private macOS bridge that turns Calendar, Things 3, and Obsidian into one clean local API for personal dashboards.
Permission-first. Local by default. Dashboard-ready.
SourceRelay is a native macOS app for people who run personal dashboards and want those dashboards to talk to the apps where their real context lives. It collects upcoming events, tasks, and selected notes into a normalized snapshot, exposes a small authenticated HTTP API, and includes a diagnostics dashboard so you can see exactly what the bridge is serving.
The app is designed to replace one-off scripts and terminal-launched bridge servers with a launchable macOS app: Dock icon, menu bar status, source access controls, saved ports, and a UI for checking whether the bridge is healthy.
The screenshots below show the native app window, the permission setup flow, the saved bridge configuration, and the compact menu bar status panel. The Obsidian vault path is intentionally hidden in the public screenshot.
| Overview | Sources |
|---|---|
![]() |
![]() |
| Bridge Settings | Menu Bar |
|---|---|
![]() |
![]() |
- Reads upcoming Calendar events.
- Reads Things 3 tasks by tag, area, project, date window, pin, or hide state.
- Reads selected Obsidian Markdown tasks and notes from a user-chosen vault.
- Normalizes everything into a single Bridge API v1 snapshot.
- Lets dashboards pin or hide items without changing the original source.
- Keeps writes disabled by default, with per-source write toggles.
- Serves a local diagnostics UI with countdown cards, source counts, overrides, Obsidian items, and raw item inspection.
- Saves bridge host, API port, diagnostics port, vault path, source rules, and write settings between launches.
SourceRelay is intended for private use on your Mac or trusted private network. Do not expose it directly to the public internet.
- The API requires an authorization credential copied from the app.
- The credential is stored in the macOS Keychain when possible.
- Settings are stored at
~/Library/Application Support/SourceRelay/settings.jsonwith private file permissions. - The diagnostics UI binds to loopback.
- Source writes are off by default.
- Calendar, Things 3, and Obsidian access are granted from the native app UI.
- macOS 14 or newer.
- Xcode Command Line Tools or Xcode.
- Swift toolchain compatible with the package.
- Optional sources:
- Apple Calendar.
- Things 3.
- An Obsidian vault stored on local disk or a synced folder.
Install Xcode Command Line Tools if needed:
xcode-select --installClone and build the app:
git clone https://github.com/alfonsopuicercus/source-relay.git
cd source-relay
mac_app/scripts/package_app.sh
open mac_app/build/SourceRelay.appOn first launch:
- Open the SourceRelay window from the Dock or menu bar.
- Go to Sources.
- Click Allow Calendar Access if you want calendar events.
- Click Allow Things Automation if you want Things 3 tasks.
- Click Choose Vault if you want Obsidian items.
- Go to Bridge.
- Choose the API host and ports.
- Click Save Settings.
- Go to Overview and click Start Bridge or Restart Bridge.
- Click Open Diagnostics to confirm data is flowing.
Once configured, SourceRelay is meant to stay out of the way:
- Launch SourceRelay.app.
- Check the menu bar icon: green means the bridge is serving data.
- Use Sync Now from the menu bar when you want an immediate refresh.
- Use Open Dashboard to inspect the diagnostics UI.
- Keep your personal dashboard pointed at the stable Bridge API URL.
Pin and hide actions from your dashboard call the Bridge API and are stored by SourceRelay as local overrides. They do not delete or rewrite the original Calendar, Things, or Obsidian item.
Use one of these paths:
- GitHub Releases: download the packaged app zip when a release is available.
- Build from source: run
mac_app/scripts/package_app.sh. - Source archive: use GitHub's Code → Download ZIP button.
If macOS warns that the app is from an unidentified developer, open it with right-click → Open. The app is not notarized unless you sign and notarize your own build.
Open Bridge in the app to edit and save these values:
| Setting | Typical value | Notes |
|---|---|---|
| API Host | 127.0.0.1 |
Local-only dashboard on the same Mac. |
| API Host | 0.0.0.0 |
Dashboard on another trusted device in your private network. |
| API Port | 8765 |
Main Bridge API port. |
| Diagnostics Port | 8766 |
Browser diagnostics UI. |
| Things writes | Off | Enables Things mutations only when switched on. |
| Calendar writes | Off | Enables Calendar mutations only when switched on. |
| Obsidian writes | Off | Enables Obsidian mutations only when switched on. |
Click Save Settings after editing host or ports. Restart the bridge for port changes to take effect.
Environment variables can override saved values for advanced launches:
SOURCE_RELAY_API_HOST=127.0.0.1 \
SOURCE_RELAY_API_PORT=8765 \
SOURCE_RELAY_DIAGNOSTICS_PORT=8766 \
open mac_app/build/SourceRelay.appPoint your dashboard at:
http://<mac-host>:8765/v1
Use the credential from Copy API Credential:
export SOURCE_RELAY_TOKEN="<copied-token>"
curl \
-H "Authorization: Bearer ${SOURCE_RELAY_TOKEN}" \
http://127.0.0.1:8765/v1/health
curl \
-H "Authorization: Bearer ${SOURCE_RELAY_TOKEN}" \
http://127.0.0.1:8765/v1/snapshotFor a dashboard running on another device:
- Set SourceRelay API Host to
0.0.0.0. - Keep the API Port stable, for example
8765. - Use the Mac's private network hostname or IP in the dashboard.
- Use the copied API credential in the dashboard configuration.
- Keep diagnostics local unless you explicitly proxy it for yourself.
All routes require:
Authorization: Bearer <copied-token>
Routes:
| Method | Route | Purpose |
|---|---|---|
GET |
/v1 |
API metadata and route list. |
GET |
/v1/health |
Minimal health check. |
GET |
/v1/snapshot |
Current selected items, all items, rules, errors, and overrides. |
POST |
/v1/refresh |
Re-read sources and return a new snapshot. |
GET |
/v1/search?q=term |
Search normalized items. |
GET |
/v1/rules |
Read current selection rules. |
PUT |
/v1/rules |
Replace selection rules when rule writes are enabled. |
PUT |
/v1/overrides/{id} |
Pin or hide a normalized item. |
POST |
/v1/mutate |
Apply source mutations when that source's writes are enabled. |
GET |
/v1/permissions/{source} |
Check source write permission state. |
Snapshot responses include:
{
"items": [
{
"id": "calendar:event-example",
"source": "calendar",
"source_id": "event-example",
"title": "Example meeting",
"start": "2026-06-24T09:00:00Z",
"end": "2026-06-24T09:30:00Z",
"status": "open",
"notes": "",
"metadata": {
"calendar": "Work"
}
}
],
"all_items": [],
"all_count": 1,
"refreshed_at": "2026-06-23T18:00:00Z",
"refresh_error": {},
"rules": {
"windows": {
"calendar": 14,
"things": 14,
"obsidian": 30
},
"things_tags": ["Dashboard"],
"things_areas": [],
"things_projects": [],
"calendars": [],
"obsidian_paths": []
},
"overrides": {}
}SourceRelay selects items in three layers:
- Overrides: pinned items are always included; hidden items are excluded.
- Source rules: Things tags, Things areas, Things projects, calendars, and Obsidian paths.
- Date windows: upcoming Calendar events, dated Things tasks, and dated Obsidian items within each configured window.
Default rules:
{
"windows": {
"things": 14,
"calendar": 14,
"obsidian": 30
},
"things_tags": ["Dashboard"],
"things_areas": [],
"things_projects": [],
"calendars": [],
"obsidian_paths": []
}Dashboards can pin or hide an item with:
curl \
-X PUT \
-H "Authorization: Bearer ${SOURCE_RELAY_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"pinned":true}' \
http://127.0.0.1:8765/v1/overrides/calendar:event-exampleUse {"hidden":true} to hide an item. Use false to clear a pin or hide state.
Overrides are stored separately from Calendar, Things 3, and Obsidian. They are dashboard preferences, not source edits.
SourceRelay is intentionally simple:
- The app reads enabled sources.
- The selection engine chooses dashboard-relevant items.
- The Bridge API serves the snapshot.
- Your dashboard pulls
/v1/snapshotor triggers/v1/refresh. - Optional pin and hide actions go back through
/v1/overrides/{id}. - Optional source writes go through
/v1/mutateonly if enabled in the app.
The default refresh interval is 300 seconds. A dashboard can also call
POST /v1/refresh when the user clicks a sync button.
The diagnostics UI is the fastest way to verify a setup:
- Header shows live state and selected item count.
- Upcoming lane shows countdown cards for selected time-based items.
- Source summary shows Calendar, Things, and Obsidian totals.
- Overrides show pinned and hidden items.
- Obsidian panel shows selected Obsidian tasks and notes.
- Raw table shows the normalized items your dashboard can consume.
- Warning banner shows source errors without exposing credentials.
Open it from the app with Open Diagnostics.
The bridge says the address is already in use. Another process is using the API or diagnostics port. Choose another port in Bridge, click Save Settings, then restart the bridge.
The port changes after restart.
Set API Port and Diagnostics Port manually in Bridge, click Save Settings,
then restart. Saved settings live under ~/Library/Application Support/SourceRelay/.
The dashboard gets 401 Unauthorized. Copy a fresh API credential from the app and update your dashboard configuration.
Calendar is empty. Open Sources, grant Calendar access, then restart or refresh the bridge.
Things 3 is empty. Open Sources, allow Things automation, and make sure the tasks match your Things tags, areas, projects, or date window.
Obsidian is empty. Choose a vault in Sources and add one or more included Obsidian paths in your selection rules. SourceRelay only reads files inside the chosen vault.
macOS asks for Keychain access repeatedly. Choose Always Allow for the SourceRelay credential. If you are rebuilding frequently, macOS may treat each build as a new binary identity.
Run tests:
swift test --package-path mac_appBuild the app bundle:
mac_app/scripts/package_app.shRun the publication scan before publishing:
scripts/publication_scan.shFor release-grade scanning, install gitleaks and run:
scripts/publication_scan.sh --releaseOptional signing:
SOURCE_RELAY_SIGN_IDENTITY="Developer ID Application: Example" \
mac_app/scripts/package_app.sh- Do not commit settings files, environment files, logs, local databases, or app support files.
- Do not commit API credentials or screenshots containing private item titles.
- Keep the API on
127.0.0.1unless a trusted private network dashboard needs it. - Rotate the API credential if you accidentally paste it somewhere public.
- Run the publication scan before pushing a public release.
MIT. See LICENSE.




