Skip to content

Declade/lucairn-sensitive-mode-client

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

152 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Lucairn Sensitive Mode — local stack

Status: Phase 0 (pre-launch). Not yet shipped to users. This repo is the open-source local-side companion to the Lucairn cloud gateway.

What is Sensitive Mode?

A menubar/tray app + local TLS-terminating daemon (lucairnd) that lets EU knowledge workers route supported AI tools on their laptop through the Lucairn sanitization gateway. In the current dogfood build, Claude Code, Cursor API mode, OpenAI-compatible API clients, and extension-backed ChatGPT.com web are the active protected surfaces. The ChatGPT.com path is intentionally non-MITM: the menubar app opens Chrome with Lucairn's bundled extension in a dedicated protected profile, and the VM release matrix smoke-tests that path through Chrome DevTools. Claude Desktop, claude.ai, ChatGPT Desktop, and Gemini stay visible as Coming Soon / diagnostic rows until their repeatable smoke gates are clean.

For the full architecture, threat model, trust posture, and roadmap, see the v1 design spec. Topic-specific documentation lives alongside it in docs/:

  • Architecture — the five interception paths, daemon internals, cloud endpoints
  • Threat model — per-install root CA primitive, trust scenarios, defenses
  • Phase 0 + v1 roadmap — build sequence, decision gates, scope estimates
  • Glossary — domain terminology + acronyms
  • FAQ — buyer-facing questions

Status

This repo is Phase 0 / pre-launch. It is private during the build and will flip public at v1 launch with full reproducible-build CI + Sigstore release signing. Right now, the repo holds:

Component scaffolds (lucairnd/, ui/, extension/, adapters/, installer/, tests/) land in subsequent PRs.

What's open source vs closed

Open source (this repo, MIT): lucairnd daemon, Tauri-based menubar app, browser extension, per-vendor chat-protocol adapters, onboarding wizard, root-CA generation logic.

Closed source (Lucairn cloud gateway at gateway.lucairn.eu): sanitization models + L3 PII shield, ID Bridge mapping store, Lucairn Witness signing, cert chain assembly, account/billing/control plane.

Rationale: every line that runs on a user's machine — i.e., everything that touches plaintext or system trust — is auditable. The closed-source backend is where Lucairn's IP moat lives. Standard precedent: Tailscale, Mullvad, Signal, ProtonVPN, Cloudflare WARP all run open-source clients + closed-source backends.

Repo layout (planned, populated incrementally)

lucairn-sensitive-mode-client/
├── lucairnd/          # Go daemon: TLS termination, vendor adapter dispatch
├── ui/                # Tauri 2 (Rust + WebView): cross-platform menubar/tray
├── extension/         # MV3 browser extension (Chrome + Safari + Firefox + Edge)
├── adapters/          # Per-vendor chat-protocol adapters (TS, shared lucairnd ↔ extension)
├── installer/         # Onboarding wizard + root-CA generation logic
├── docs/              # Design spec, threat model, architecture, FAQ
├── tests/             # Integration tests + reproducible-build verification
└── .github/workflows/ # Public CI: deterministic builds + Sigstore signing

Building

(Build instructions land per-component when each scaffold PR ships.)

Production install (auto-start via launchd)

On macOS, the lucairnd daemon must run as root because it binds the privileged port 127.0.0.1:443 (so AI tools' default HTTPS traffic can be intercepted locally). For day-to-day use we don't want the user opening a terminal and typing sudo every laptop boot — a launchd LaunchDaemon handles auto-start.

The preferred dogfood routing mode is transparent PF routing: supported API clients keep resolving the real vendor IPs for supported Anthropic, OpenAI API, and Claude consumer-chat hosts, while macOS routes those IPs to local lucairnd through a managed PF anchor. ChatGPT Desktop and OpenAI consumer hosts stay unrouted by default because the official desktop client rejects local TLS inspection before chat can run; protected ChatGPT work is handled through the browser-extension path instead of the PF/TLS layer. OpenAI API routing is for API clients on api.openai.com, not for the account-backed ChatGPT Desktop app. The older /etc/hosts loopback block remains as fallback/cleanup compatibility, but new wizard and Settings flows use the transparent PF path first.

The menubar's Open protected ChatGPT.com action launches Chrome or Chrome for Testing with Lucairn's bundled unpacked extension and a dedicated Lucairn Protected ChatGPT browser profile. This keeps the supported ChatGPT consumer path explicit instead of opening the user's normal browser and implying that any chatgpt.com tab is protected.

ChatGPT.com web smoke test

For release smoke, run the signed-in protected ChatGPT.com profile with Chrome remote debugging enabled, then run:

script/chatgpt_web_smoke.mjs \
  --cdp-url http://127.0.0.1:9222 \
  --report artifacts/chatgpt-web-smoke/latest.json

The smoke test submits a real prompt, asserts that the actual /backend-api/f/conversation POST contains a Lucairn placeholder and not the local sensitive value, checks that the visible response contains the local value with no placeholders, reloads the conversation, and repeats the visible placeholder check. Native ChatGPT Desktop remains outside v1 support because the app rejects local TLS inspection before chat can run.

The VM app matrix includes the same harness. It launches Chrome or Chrome for Testing with the dedicated Lucairn Protected ChatGPT profile, Lucairn's extension, and DevTools on LUCAIRN_CHATGPT_WEB_CDP_URL when no debug browser is already reachable:

script/vm_app_matrix.sh --chatgpt-web-smoke

The profile still has to be signed into ChatGPT for a real conversation smoke. Use --chatgpt-web-profile-dir DIR when you intentionally want a different signed-in test profile, or --skip-chatgpt-web-launch when a browser is already running with DevTools exposed.

Claude.ai web smoke test

script/claude_web_smoke.mjs is the matching opt-in harness for the extension-backed claude.ai path. It drives a signed-in protected Chrome profile through DevTools, submits a real Claude prompt, and asserts that the outgoing /api/organizations/.../chat_conversations/.../completion POST is placeholder-redacted while the visible answer is locally relinked.

The VM matrix keeps this smoke off by default while claude_browser remains a diagnostic / Coming Soon row. To require it after signing into claude.ai in the protected Chrome profile, run:

script/vm_app_matrix.sh --claude-web-smoke

Install

The setup wizard's last screen (Step 5 — Routing) carries an "Auto-start at login" checkbox. With it ticked (default ON), clicking Continue:

  1. Creates ~/Library/Logs/LucairnSensitiveMode/ AS THE USER (no admin prompt; the user already owns ~/Library/Logs/). A symlink-safety check refuses to install if the path already exists as a symbolic link — closes a same-UID pivot to a system directory.
  2. Fires an osascript admin prompt that ONLY: a. Writes the LaunchDaemon plist to /Library/LaunchDaemons/eu.lucairn.sensitivemode.plist (root-owned 0644). b. chmod 644 the plist. c. launchctl bootstrap system /Library/LaunchDaemons/eu.lucairn.sensitivemode.plist.

Re-running the install path on an already-loaded daemon is idempotent — the plist is overwritten and launchctl bootstrap exits 37 ("Service is already loaded"), which we map to ok rather than a red error banner.

The plist:

  • Label: eu.lucairn.sensitivemode
  • UserName: root (required for :443 bind)
  • RunAtLoad: true (auto-start at boot)
  • KeepAlive: true (restart-on-crash)
  • EnvironmentVariables.LUCAIRN_LISTEN_ADDR: 127.0.0.1:443
  • EnvironmentVariables.SUDO_UID / SUDO_GID: the invoking user's UID/GID, so the H-2 file-ownership preservation logic chowns rotated state files (control-token, conversations.db, provenance.db) back to the user even though launchd (NOT sudo) spawned the daemon.

Verify

launchctl list | grep lucairn
# Expect: <pid> 0 eu.lucairn.sensitivemode

launchctl print system/eu.lucairn.sensitivemode | head
# Expect: state = running

Uninstall

The menubar's Sensitive Mode settings panel carries an "Uninstall auto-start" button. Clicking it fires an osascript admin prompt that:

  1. launchctl bootout system /Library/LaunchDaemons/eu.lucairn.sensitivemode.plist (silenced; returns 36 when the daemon isn't loaded — benign for uninstall).
  2. rm -f /Library/LaunchDaemons/eu.lucairn.sensitivemode.plist.

Both steps are idempotent: re-running uninstall after a successful uninstall is a no-op (returns 0).

Why LaunchDaemon and not LaunchAgent

LaunchAgent in ~/Library/LaunchAgents/ runs as the user and can't bind :443. LaunchDaemon in /Library/LaunchDaemons/ (root-owned) runs system-wide and can specify UserName=root. Auto-start of :443 thus requires LaunchDaemon. The H-2 chown-back logic ensures user-created state files don't inherit root ownership.

Multi-user macOS (limitation)

In v1, auto-start is per-installer. The user who runs the wizard's "Auto-start at login" checkbox is the user whose ~/Library/Logs/ LucairnSensitiveMode directory the daemon writes to, and whose UID/GID the daemon chowns back to via the H-2 file-ownership preservation plumbing. On a shared workstation with multiple macOS user accounts, only ONE user can have the LaunchDaemon's SUDO_UID / SUDO_GID set to their account at a time — the daemon will run system-wide but only owns its state files for the installer. Multi-user shared workstations are not supported in v1; teams that need per-user isolation should run the daemon on each user's laptop separately. A multi-user-aware path (per-user LaunchAgent on a non-privileged port + a single shared root LaunchDaemon for :443 that fans out via UID-aware routing) is a follow-up workstream.

Linux + Windows

Auto-start on Linux (systemd user units / polkit-fronted helper) and Windows (Service Control Manager) are queued as follow-up workstreams (LSM-launchd-linux / -windows). On those platforms the wizard currently surfaces a platform_unsupported envelope; users run lucairnd manually until the per-platform paths land.

Contributing

See CONTRIBUTING.md. The repo is private during Phase 0 / v1 build; broader contribution opens at v1 launch.

License

MIT — see LICENSE.

About

Lucairn Sensitive Mode — local stack (lucairnd daemon + Tauri menubar app + browser extension + per-vendor adapters). MIT-licensed, open-source companion to gateway.lucairn.eu.

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors