36 — Notifications & Background Work
Many useful Locara apps need to keep running when their main window is closed. A meeting recorder transcribes a long talk while the user takes notes elsewhere. A document indexer processes a folder overnight. A notification surfaces when work completes.
This document specifies how background work and notifications fit into the capability model and runtime.
Core principles
- Capability-gated, like everything else. Background work and notifications are declared in the manifest; the runtime enforces.
- User-visible, never silent. Apps in the background show a status-bar item and a progress indicator. No invisible background processes.
- Battery-aware. The runtime respects macOS background-task scheduling; heavy work pauses when the system signals power constraints.
- Graceful on quit. Apps can save state when the user explicitly quits Locara or shuts down the Mac.
Capabilities
Two new capabilities for v2 of the manifest schema (or v1 with these as additions):
"capabilities": {
"background": {
"mode": "active" | "passive" | false,
"reasons": ["transcription", "indexing", "agent-loop"]
},
"notifications": {
"modes": ["alert", "badge", "sound"]
}
}
background.mode
| Value | Behavior |
|---|---|
false | App stops when its window closes. Default. |
"passive" | App keeps a minimal status-bar item; can run light periodic tasks (every N minutes); cannot do continuous heavy work. |
"active" | App keeps full background process; can do continuous work (live transcription, ongoing inference). User sees a “Locara: Transcribe is recording” status-bar indicator. |
reasons is a free-form list of strings that the runtime surfaces to the user: “Transcribe is running in the background to: transcription, agent-loop.” Honesty principle.
notifications.modes
| Mode | What it does |
|---|---|
alert | Banner / alert notification |
badge | Dock icon badge count |
sound | Notification sound |
Apps declare what kinds of notifications they want. macOS still requires user grant via TCC the first time; declaration just signals intent.
Background lifecycle
Entering background
1. User closes the main window.
2. App's manifest declares background.mode != false.
3. Runtime keeps the app process alive.
4. Status-bar icon appears (unique per running app).
5. Tauri `MainWindow.hide()` semantics; window can be reopened from status bar.
Active background work
While in background:
- App can call SDK normally.
- Inference, storage, tool execution all work.
- Network access (if declared) works.
- macOS may throttle CPU under low-power conditions.
- App should periodically yield (
await new Promise(r => setTimeout(r, 0))) to remain responsive.
Exiting background
Three exit paths:
- User reopens the window — app comes to foreground; background mode pauses if applicable.
- User explicitly quits the app — runtime calls a
beforeQuithook; app has 5 seconds to save state; then process is killed. - System shutdown / restart — same as user quit.
- Resource pressure — runtime may terminate background apps under critical memory pressure (warns user).
Dock vs status-bar visibility
Apps can choose:
- Dock-visible — standard Mac app behavior. Background = window hidden but Dock icon present.
- Status-bar only — no Dock icon; only the status-bar indicator. Useful for utility apps. Declared in manifest.
"presentation": {
"dock_icon": true, // default
"status_bar_item": "auto" // auto = visible only when in background
}
Notification API
import { notifications } from '@locara/sdk'
await notifications.show({
title: "Transcription complete",
body: "Your meeting from 2pm is ready.",
sound: "default",
// Action buttons (max 2)
actions: [
{ id: "open", label: "Open" },
{ id: "dismiss", label: "Dismiss" }
],
// Optional: deep-link back into the app
payload: { transcriptId: "abc123" }
})
// Listen for action clicks
notifications.onAction((event) => {
if (event.id === "open") {
// Bring app to front, navigate
}
})
// Update Dock badge
notifications.setBadge(5)
notifications.clearBadge()
Behind the scenes: the SDK calls a Tauri plugin that uses macOS’s NSUserNotificationCenter (or UNUserNotificationCenter).
Permission flow
- Manifest declares
notifications.modes. - First time the app calls
notifications.show(...), macOS prompts the user via TCC: “Locara: Transcribe wants to send notifications.” - User grants or denies.
- If denied: subsequent calls fail silently (or return error per SDK design).
- User can manage in System Settings → Notifications.
Periodic / scheduled tasks
For apps that want to run periodically (not continuously):
import { schedule } from '@locara/sdk'
// Run a function every hour while the app is in passive background mode
schedule.repeat({
id: "index-new-documents",
intervalSeconds: 3600,
handler: async () => {
await indexDocuments()
}
})
// Or schedule a one-shot at a specific time
schedule.at({
id: "morning-summary",
when: tomorrow_8am,
handler: async () => {
await generateMorningSummary()
}
})
Implementation: the runtime maintains a per-app scheduler that wakes the app when due. Subject to macOS’s NSBackgroundActivityScheduler semantics — the system may delay tasks to consolidate wake-ups.
Constraints:
- Minimum interval: 5 minutes (avoid wake-up spam).
- Maximum runtime per task: 30 seconds (then suspended).
- No guaranteed exact timing — system optimizes for battery.
For longer or unbounded background work, use background.mode: "active".
Battery + power awareness
The Locara runtime hooks into macOS’s power-state notifications. App authors can subscribe:
import { power } from '@locara/sdk'
power.onLowPower((isLowPower) => {
if (isLowPower) {
// Pause heavy inference
pauseTranscription()
}
})
power.onPlugged((isPlugged) => {
// Take advantage of being plugged in
})
The runtime additionally throttles inference automatically under critical battery:
- Below 20% battery: pause non-essential apps’ active background work.
- Below 10%: strongly throttle even foreground inference; surface to user.
Apps can override with explicit user consent (“This will use battery”).
What background apps cannot do
Even with background.mode: "active" declared:
- Cannot escape capability declarations. A background app with
net: falsestill has no network. - Cannot wake other apps. No cross-app “wake on event” except through the explicit IPC shared folders.
- Cannot keep the system awake indefinitely. Apps that need to prevent sleep must declare
power.prevent_sleep(separate capability) and the runtime surfaces this to the user prominently. - Cannot run after explicit user quit. No daemonization.
Status-bar UX
Each running background app appears as a separate status-bar icon. Click → mini-menu:
┌─────────────────────────────────┐
│ Transcribe │
│ ─ Recording (00:42:13) │
│ ─ Pause │
│ ─ Open │
│ ─ Quit │
└─────────────────────────────────┘
The runtime provides a default status-bar UI; apps can customize via the SDK if needed.
macOS’s standard menu-bar shows each running app’s status-bar item independently. The optional Locara Manager utility (phase 3+) could aggregate this into a single “3 Locara apps running in background” surface; in v1, users see each app’s own status-bar item.
Power-prevent-sleep capability
For apps that need to keep the Mac awake (long transcription jobs, agent loops):
"capabilities": {
"power.prevent_sleep": true
}
Triggers prominent user consent at install + during use. macOS surfaces these via the standard “App is preventing sleep” indicators.
Dogfooding considerations
The reference apps will demonstrate:
- Transcribe uses
background.mode: "active"while recording,falseotherwise. Shows status-bar timer. - DocVault uses
background.mode: "passive"to do periodic re-indexing of watched folders. - Both use
notificationsmodestly (completion alerts only).
Performance budgets in background
Background apps still have RAM budgets but slightly relaxed CPU expectations — they should yield more often, run lighter inference variants when possible, batch work to avoid wake-ups.
Each app’s settings show its own background-mode usage; users can disable background mode per app from there. The optional Locara Manager utility (phase 3+) could aggregate cross-app warnings.
Open questions
- (open) Should background apps be able to schedule work for “the next time the user opens this app”? Useful for indexing, etc. Probably yes.
- (open) Cross-platform: Linux’s notification + background story is messy (multiple desktop environments). Windows is more uniform. Defer to platform-specific implementations.
- (open) Apple Intelligence integration: macOS Tahoe (or later) may expose system-level “Live Activities”-style background work APIs. Worth integrating when stable.
- (open) Should we expose a “low-power mode” capability that signals an app intends to be respectful of battery? More of a hint than enforcement.
Cross-references
- Capability model: 03-capabilities.md
- Runtime architecture: 07-runtime.md
- Resource policy: 32-resource-policy.md
- Performance budgets: 21-performance-budgets.md
- Distribution / install consent: 15-distribution.md