13 — Security & Privacy
This is the synthesis doc for how the Locara design adds up to “secure and private by default.” It cross-references the implementation details that live in other docs and shows how each layer composes.
The wedge of Locara is defensible privacy — apps that demonstrably can’t betray the user. This is not just a registry policy; it’s a property of the architecture. This document explains why.
The “secure and private by default” thesis
A user installing a Locara app should be able to assume:
- The app cannot see anything outside its declared scope. Not other apps’ data, not arbitrary filesystem, not the network unless explicitly allowed.
- Nothing the app does leaves the device unless the manifest says so. No telemetry, no analytics, no remote logging, no model-fetch leaking content, no error reporting.
- The Locara framework itself does not phone home. Zero ambient telemetry from the runtime, the CLI, the dev panel, or the client.
- Data the app stores stays on disk in a per-app container, encrypted at rest by macOS FileVault (and optionally by the app via SQLCipher).
- What the app can do is verifiable — at install time, before clicking, the user sees exactly the capability surface and can compare it to what the app has declared.
These five properties are not promises. Each is enforced by an architectural choice. Below is each property mapped to its mechanism.
Privacy by architecture (the local-first dividend)
The “fully local” thesis isn’t just a marketing line — it’s a privacy property that emerges from where reads and writes go:
| Operation | Where it goes | Privacy implication |
|---|---|---|
| LLM inference | Local weights, local memory | Prompts and outputs never leave the device |
| Embeddings | Local model | Texts being embedded never leave the device |
| Transcription | Local Whisper | Audio never uploads anywhere |
| OCR | Local OCR model | Document content stays on disk |
| Database reads/writes | Per-app SQLite file | All structured data is local |
| Vector search | Local sqlite-vec / zvec | Queries + matched content stay local |
| File reads | macOS Powerbox-mediated | Only files the user explicitly picks |
| Tool execution | Wasmtime sandbox | Tools can only do what their imports allow |
| Logging | ~/Library/Logs/Locara/ | On disk, on device |
| Error reporting | None (no auto-reporting) | User must explicitly export logs |
| Update check | Manifest check via HTTPS to registry | Only “do you have a newer version?” — no app-internal data |
Compare to a typical SaaS AI app:
| Operation | Where it goes |
|---|---|
| LLM inference | Vendor’s GPU cluster |
| Embeddings | Vendor’s model server |
| Transcription | Vendor’s transcription API |
| OCR | Vendor’s OCR service |
| Database reads/writes | Vendor’s PostgreSQL |
| Vector search | Vendor’s vector DB |
| File uploads | Vendor’s S3 |
| Tool execution | Vendor’s compute |
| Logging | Vendor’s observability stack |
| Error reporting | Sentry / Datadog / etc. |
| Telemetry | Mixpanel / Amplitude / etc. |
Every line in the SaaS column is a place data could leak, be subpoenaed, be breached, be analyzed for advertising, or be retained beyond what the user expects. In the Locara column, every line stays local by default.
This is the structural privacy advantage of local-first. It’s not “we promise not to look at your data” — it’s “your data is never on a system we operate in the first place.”
The “fully local” badge formalizes this: an app earns it when net: false, all models locally cached, and no tools requiring network. Auto-computed from manifest. See 03-capabilities.md.
Defense in depth — the layered security model
┌──────────────────────────────────────────────────────────┐
│ Layer 6: User consent (informed install + capability UI) │
├──────────────────────────────────────────────────────────┤
│ Layer 5: Capability cool-down on update │
├──────────────────────────────────────────────────────────┤
│ Layer 4: Locara registry review + signing + provenance │
├──────────────────────────────────────────────────────────┤
│ Layer 3: Locara runtime capability enforcement │
├──────────────────────────────────────────────────────────┤
│ Layer 2: Tauri IPC trust boundary (capability-gated) │
├──────────────────────────────────────────────────────────┤
│ Layer 1: macOS App Sandbox (kernel enforcement) │
└──────────────────────────────────────────────────────────┘
Each layer denies independently. Bypassing all six requires:
- A bug in macOS kernel (Apple’s responsibility, frequently patched).
- A bug in Tauri’s IPC layer.
- A bug in Locara’s plugin enforcement.
- A bug in Locara’s review pipeline.
- A bypass of the cool-down clock.
- A user explicitly clicking through clear consent UI.
This is what “defense in depth” actually means: not “we have lots of checks” but “an attacker has to compromise multiple independent systems.”
Secure-by-default checklist
Every default in Locara is the safe option. Here is what that looks like, surface by surface:
Manifest defaults
| Field | Default | Rationale |
|---|---|---|
capabilities.net | false | Most apps don’t need network. Earns “fully local” badge. |
capabilities.fs.user-selected | read (when omitted, derived as needed) | Powerbox-mediated; user explicitly picks files. |
capabilities.device.* | all false | TCC prompts only triggered if declared and used. |
capabilities.tools | [] | No code-exec without explicit declaration. |
capabilities.ipc | [] | No inter-app communication by default. |
storage.backups.enabled | false | No backups created without opt-in. |
| Custom tools | rejected unless reviewed | Only registry-curated tools by default. |
Runtime defaults
| Behavior | Default | Rationale |
|---|---|---|
| Model fetching | Locara runtime fetches; app cannot do its own | Apps never need network for model loading; can’t sneak in extra fetches. |
| Telemetry | None, ever | Privacy-as-pitch demands zero phone-home. |
| Crash reports | Stored locally; user must export to share | No automatic sentry-style upload. |
| Update checks | Manifest-only request to registry; no app-internal info | Daily by default; user can disable. |
| Logging | Local file under ~/Library/Logs/Locara/ | Logs never leave the device. |
| Cache | Per-app + shared model cache, on disk | Cleared on uninstall (option to keep). |
CLI defaults
| Behavior | Default | Rationale |
|---|---|---|
locara doctor reporting | Local output only; no upload | Diagnostics stay on dev machine. |
locara init --from-prompt | Uses dev’s own API key from env | Locara doesn’t proxy LLM calls. |
locara test capability tests | Required to pass before publish | Static check on what the app actually does. |
locara verify --strict | Treats warnings as errors | CI uses this; prevents accidental capability creep. |
Build defaults
| Behavior | Default | Rationale |
|---|---|---|
| Code signing | Required | Gatekeeper compatibility. |
| Notarization | Required | Apple-vetted malware scan. |
| Provenance attestation | Always generated, always logged | Publicly verifiable supply chain. |
| Build environment | Fresh ephemeral runner | No environment carryover. |
| Source-only submission | Required (no developer-uploaded binaries) | Eliminates “compromised dev machine” attacks. |
Registry defaults
| Behavior | Default | Rationale |
|---|---|---|
| Publisher identity | GitHub OAuth required | Cheap identity floor. |
| 2FA | Required for publishing | Account-takeover mitigation. |
| Auto-approve | Only for low-risk capability profiles | Risk-based routing. |
| Capability cool-down | 7 days for capability expansions | Account-takeover mitigation. |
| Review evidence | Public | Trust through transparency. |
| Kill-switch | Available, signed, logged | Forensics + revocation. |
Distribution defaults
| Behavior | Default | Rationale |
|---|---|---|
| Auto-update | User opt-in per app | Privacy + control. |
| Capability-expanding update | Requires re-consent + cool-down | Account-takeover mitigation. |
| Sideloaded apps | Signature verification required | Same trust mechanics for off-registry installs. |
| Multi-registry | User must explicitly add non-default registries | Default registry is the trusted source. |
| App data on uninstall | Kept by default | User chooses to wipe. |
What you’ll notice: every default is “safer / more private,” and any deviation requires explicit user action with clear consequences.
What the user must opt into for less safety
The asymmetry matters: the path to “less safe” is explicit, slow, and visible.
| Action | Friction |
|---|---|
| Install an app that uses network | Clear capability summary at install + ongoing badge in the app itself |
| Add an alternative registry | Manual locara registry add; warning shown in client |
| Install a sideloaded app | ”This is a sideloaded app, not from the registry” banner |
| Disable auto-updates | Per-app or global setting; doesn’t affect security updates |
| Enable subprocess tool exec | Manifest declaration; flagged in review; prominent warning to users |
| Skip notarization | Not possible — Locara CI requires notarization |
The user can always make the system less private (e.g., install a network-using app), but they always know they’re doing so. Locara never silently enables a less-safe behavior.
Threat model summary
A high-level summary; full details are in 14-trust-safety.md.
What we defend against
- Malicious app at submission. Verified publisher + automated review + capability declarations + runtime enforcement.
- Compromised publisher account. Mandatory 2FA + capability cool-down + kill-switch.
- Compromised dev machine. Source-only submissions + Locara CI builds in clean environment.
- Tampered artifact in transit/at rest. Sigstore provenance + dual signatures + transparency log.
- Capability creep across updates. Mandatory cool-down + re-consent on capability expansion.
- Tool-mediated escape. Wasmtime sandbox + composition rule (tool ≤ app capabilities).
- Network exfiltration. macOS App Sandbox kernel-blocks network when
net: false. - Filesystem traversal. Powerbox-mediated user-file access; container isolation.
- Cross-app contamination. Per-app containers; explicit IPC declarations.
- Supply-chain attacks via models/tools. Content-addressed by SHA; verified at install.
What we don’t defend against
- User explicitly granting all permissions and installing a clearly-marked-malicious app. No system can save users from informed self-harm.
- Physical access attacks (someone steals the laptop with FileVault disabled). Out of scope; this is OS-level concern.
- Side-channel attacks on the Apple Silicon GPU during inference. Theoretical research; no practical exploits known.
- Apple itself. Locara apps run on Apple’s OS; Apple has theoretical access. The privacy floor is “Apple ≤ Locara” — we don’t try to defend against Apple.
- Hardware compromise (firmware-level malware). Out of scope.
- Someone publishing a copycat app on a different store that lies about being Locara-built. Brand defense, not technical.
Privacy-specific design choices
Beyond the capability/sandbox machinery, several design choices contribute specifically to privacy:
Zero ambient telemetry
The Locara CLI, runtime, dev panel, and client never make outbound network calls except:
- Each Locara app’s periodic update check (manifest fetch from registry’s manifest API), via Tauri’s standard updater plugin.
locara init --from-prompt(dev’s own LLM API key, dev’s own machine).locara publish(push submission to registry).
No analytics, no error tracking, no usage statistics, no hello-world ping on launch. The compromise here would be fatal — an “anonymized telemetry” leak undermines every promise we make to users about apps.
If we ever need usage data (for product improvement), it must be:
- Opt-in.
- Aggregated with differential privacy at the source.
- Documented in a public spec document.
- Auditable by users (they can see what’s about to be sent).
This is a high bar; it should remain unmet for v1.
Data retention
Locara doesn’t retain user data because Locara never sees it. The registry stores:
- Manifest entries (public).
- App ratings + reviews (public).
- Publisher identities (verified, semi-public).
That’s it. No user identities, no install traces, no per-user activity logs.
Each Locara app stores its own user-specific state locally in its sandboxed container. There is no central “Locara client” tracking installed apps; macOS’s own app registry (Launch Services) is the source of truth, just like for any other Mac app. Nothing is synced to any server. Loss of local state = loss of state (Time Machine + per-app export are the recovery paths).
Logs and diagnostics
Logs live at ~/Library/Logs/Locara/. They include:
- Capability denials.
- Model load events.
- Runtime errors.
They do not include:
- App content (transcripts, documents, prompts).
- User identifiers.
- Network destinations (since most apps don’t use network).
When a user shares a log to debug an issue, they share a file from their disk, voluntarily, with whoever they choose. Locara never auto-collects them.
Crash reports
Crashes log a stack trace + minimal context to local logs. No automatic upload. The user can run locara doctor --export-logs to bundle logs for sharing if they choose.
What the registry can and cannot see
The Locara registry knows:
- Who published which app (publisher account → app).
- Manifest content of every published version (it’s public).
- Aggregate install counts (computed from update-check pings; not per-user).
- Public ratings + reviews.
The registry does not know:
- Which user has which app installed.
- What an app does after install.
- Any content the user generates.
- Which models a particular user has cached.
- IP-tied install patterns (CDN may have logs; we ensure they’re not retained beyond ops needs).
This is a deliberate ceiling — the registry is structured to know as little as possible about end users.
Cryptographic primitives
Stable choices for the long-term:
| Use | Algorithm | Notes |
|---|---|---|
| Hashing | SHA-256 | Industry standard; used for model + artifact addressing. |
| Code signing | Apple Developer ID (RSA / ECDSA over P-256) | Required by macOS. |
| Provenance signing | Sigstore (ECDSA over P-256, short-lived certs) | Industry-trending standard for OSS supply chain. |
| Transport | TLS 1.3 | All registry / CDN traffic. |
| Cert pinning | Each app’s runtime pins the Locara registry CA | Defends against rogue CA / MITM. |
| At-rest encryption | macOS FileVault (default) + optional SQLCipher per-app | App opts into SQLCipher if it has unusually sensitive data. |
These are mostly inherited from macOS / Sigstore / TLS — Locara doesn’t invent crypto.
Disclosure & response process
When a vulnerability is reported:
- Reporting channel:
security@locara.appwith PGP key + Sigstore identity for verification. - Triage: Severity classification (low / medium / high / critical) within 48 hours.
- Response:
- Critical: hotfix + kill-switch within 24 hours; advisory immediately.
- High: patch within 7 days; advisory + CVE if applicable.
- Medium: patch in next release cycle.
- Low: tracked publicly in issues.
- Postmortem: Public writeup published within 30 days for any non-trivial incident.
- Coordinated disclosure: Embargo periods supported when multiple parties are affected.
A security advisory page on locara.app/security lists all advisories, similar to Mozilla’s MFSA page.
(open) Bug bounty program. Likely worth doing once user base justifies it (phase 4+). Joint with Sigstore / Apple bounty programs where applicable.
What would break the privacy thesis
Things we must never do:
- Add telemetry to the framework / runtime / CLI / client. Even “anonymous.” Even “opt-out.” Even “for debugging.” It compounds and erodes trust irreversibly.
- Allow apps to bypass capability declarations via dynamic code loading. The “self-contained” rule (no remote code execution) is non-negotiable.
- Give the registry visibility into user activity. Manifest checks should be cacheable + anonymous, never logged per-user.
- Centralize keys in a way that compromise of Locara’s infrastructure breaks all signed apps. Use short-lived Sigstore certs; rotate regularly.
- Hide failures. If a malicious app slipped through, publish it. Trust is built by transparency in failure.
- Sell anonymized usage data. Even framed as “improving the product.” Just no.
- Add a “cloud sync” feature that requires apps to upload data anywhere. Apps that want sync can build it (declaring
net); the framework doesn’t.
These are guardrails. If a future maintainer is tempted to violate them under business pressure, they should treat that pressure as a signal to step back, not a justification to compromise.
Cross-references
- Capability model (the security primitive): 03-capabilities.md
- Modalities + tooling expansion: 04-modalities.md
- Runtime enforcement details: 07-runtime.md
- Tool sandbox details: 10-tools.md
- Operational trust mechanics (registry, signing, takedowns): 14-trust-safety.md
- Build pipeline (signing, notarization, attestation): 16-build.md
- Distribution (install / update consent flow): 15-distribution.md
- Storage isolation: 08-storage.md
- npm supply chain lessons:
../notes/npm.md - macOS sandbox notes:
../notes/mac-app-store-sandbox.md - Deno permission model:
../notes/deno-permissions.md