Laya is an open-source, local-first AI notification command center that aggregates Slack, Gmail, GitHub, Jira, Notion, Outlook, Calendar (and more) notifications using local LLMs via Ollama and LM Studio. Supports cloud models via BYOK.
Python
253
380 commits
updated Sep 24, 2026

A cadence for professional orchestration.
Laya is an open-source, local-first AI notification command center that aggregates Slack, Gmail, GitHub, Jira, Notion, Outlook, and Calendar notifications — powered by local LLMs via Ollama and LM Studio, or cloud models like Claude and GPT with your own API keys. It intercepts events from your professional tools, performs autonomous research and action-staging using LLM-powered agents, and presents you with ready-to-approve Action Cards -- so the answer is ready before you open the notification.
Works with:
Aggregates:
Your Tools (Jira, Slack, Gmail, Bitbucket, Calendar)
|
v
n8n (local Node.js) -- normalizes events
|
v
Laya Engine (Python) -- classifies, researches, stages
|
v
Laya UI (Tauri + Svelte) -- Action Cards you approve or dismiss
|
v
n8n -- executes approved actions (creates PRs, sends replies, etc.)
agent/<id>/<model> (Claude Code, Codex, Gemini, or Pi) for any stage. Claude Code enforces JSON schemas natively; other agents use best-effort schema + retrycards_fts/events_fts), merged via Reciprocal Rank Fusion, so both semantic and exact-keyword matches surfacehost field (empty or the cloud domain ⇒ cloud)| Layer | Technology |
|---|---|
| Desktop Shell | Tauri v2 (Rust) |
| Frontend | Svelte 5 (runes) + Skeleton UI + Tailwind CSS v4 |
| Backend | Python 3.10+ / FastAPI / asyncio |
| LLM Interface | LiteLLM (supports Anthropic, OpenAI, Google, Ollama) |
| Integration Gateway | n8n (local Node.js on port 45678) |
| Structured Storage | SQLite (async via aiosqlite, WAL mode) |
| Vector Storage | ChromaDB (embedded PersistentClient) |
| Embeddings | ONNX (built-in to ChromaDB) or sentence-transformers (optional) |
| Coding Agents | Claude Code / Gemini CLI / OpenAI Codex CLI / Pi CLI (usable as workspace agents and as inference backends) |
laya/
├── engine/ # Python FastAPI backend
│ ├── laya/
│ │ ├── main.py # Entry point (uvicorn server on :8420)
│ │ ├── config.py # Settings, paths, agent detection
│ │ ├── api/ # REST + WebSocket endpoints (27 routers)
│ │ ├── db/ # SQLite (+ FTS5) + ChromaDB + 70 migrations
│ │ ├── pipeline/ # Event processing (ingest → route → stage → emit → trace → learn → context_learn → omni)
│ │ ├── llm/ # LiteLLM client, agent inference backends, prompts, tools
│ │ ├── agents/ # Coding agent adapters (Claude, Gemini, Codex, Pi)
│ │ ├── workers/ # Multi-persona LLM workers (engineer, comms, ops, sales, hr, finance)
│ │ ├── egress/ # Outbound action execution (9 platforms)
│ │ ├── integrations/ # n8n bootstrap & client
│ │ └── security/ # OS keychain integration
│ ├── requirements.txt # Core Python dependencies
│ └── requirements-ml.txt # Optional: torch + sentence-transformers
│
├── ui/ # SvelteKit + Tauri desktop app
│ ├── src/ # Svelte 5 frontend (runes syntax)
│ │ ├── routes/ # Pages (feed, coherence, dashboard, settings, workspace, omni)
│ │ ├── lib/ # Components, API client, stores
│ │ ├── app.css # Tailwind v4 + theme system
│ │ └── app.html
│ ├── src-tauri/ # Rust/Tauri shell
│ │ ├── src/
│ │ │ ├── lib.rs # Tauri setup, commands, health polling, tray
│ │ │ ├── sidecar.rs # Python venv lifecycle & engine spawning
│ │ │ └── n8n.rs # n8n process management
│ │ ├── tauri.conf.json # Tauri config (resources, icons, window)
│ │ └── resources/ # Bundled engine source (production builds)
│ ├── package.json
│ └── svelte.config.js # Static adapter (SPA mode)
│
├── n8n/
│ └── workflows/ # Integration workflows (JSON, ~21 files: ingestion + executor per platform)
│
├── scripts/
│ ├── setup-dev.sh # One-time dev environment setup
│ ├── dev.sh # Start engine + Tauri dev server
│ ├── build.sh # Production build
│ └── update_icons.sh # Icon generation
│
├── landing/ # Landing page
└── docs/ # Architecture & design documents
The fastest way to try Laya is a prebuilt release — no toolchains required.
Open the Releases page and download the installer for your platform:
| Platform | Download |
|---|---|
| macOS | .dmg (universal — Apple Silicon + Intel) |
| Windows | .msi or .exe |
| Linux | .deb or .AppImage |
Install and launch. You do not need Python, Node, or Rust installed to run a release build. On first run, Laya checks for a compatible Python (3.10+) and Node.js (20+) already on your machine and uses those if found; otherwise it provisions its own bundled runtimes. Either way, a local n8n instance is set up under ~/.laya/.
Add an API key (Anthropic, OpenAI, Google, …) or point Laya at a local Ollama / LM Studio endpoint, then connect your tools from Settings.
macOS: release builds are signed, so they open normally — just double-click to launch.
Want to build from source, hack on the engine, or contribute? Follow the Development setup below.
You need three runtimes installed. Here's how to get each one:
brew install python@3.12 (or download from python.org)sudo apt install python3 python3-venv python3-pipVerify: python3 --version
brew install nodecurl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - && sudo apt install -y nodejsVerify: node --version && npm --version
Install via rustup:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Verify: cargo --version
macOS:
xcode-select --install
Linux (Ubuntu/Debian):
Tauri v2 requires system libraries for GTK, WebKit, and app-indicator support:
sudo apt install -y libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev patchelf
The default Linux inotify file watcher limit (65,536) can be too low for this project -- Vite needs to watch the source files while the Rust target/ directory consumes most of the quota, causing Tailwind CSS to silently fail to generate utility classes. Increase the limit:
# Immediate (resets on reboot)
echo 524288 | sudo tee /proc/sys/fs/inotify/max_user_watches
# Permanent
echo 'fs.inotify.max_user_watches=524288' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
scripts/setup-dev.sh
This script does the following:
python3, node, npm, and cargo are availableengine/.venv/ and installs dependencies from engine/requirements.txtui/node_modules/)~/.laya/n8n_module/~/.laya/data/ and ~/.laya/logs/scripts/dev.sh
This starts two processes:
python -m laya.main (with hot reload) at http://127.0.0.1:8420npx @tauri-apps/cli dev which starts Vite at http://localhost:5173 and opens the Tauri windown8n is managed automatically by the Tauri app -- it starts on launch (port 45678) and stops on quit.
Note: If the engine fails with "Address already in use", a stale engine process may be holding port 8420. The engine will attempt to kill it automatically on startup.
On first launch, the engine creates config files in ~/.laya/:
| File | Purpose |
|---|---|
settings.json | Models, agent paths, privacy settings, pipeline params |
team.json | Team member context |
rules.json | Event filtering rules |
repos.json | Git repository paths and metadata |
API keys (Anthropic, OpenAI, Google, etc.) are stored securely in your OS keychain and can be configured through the Settings UI.
The engine logs at INFO by default. Change verbosity from Settings → Data → Engine Log Level (DEBUG / INFO / WARNING / ERROR) — this maps to the logging.level key in settings.json and applies immediately, no restart. Set it to WARNING to record only warnings and errors and keep logs small. For a single run you can override it with the LAYA_LOG_LEVEL environment variable, which takes precedence over the setting (and also sets uvicorn's request-log level).
Laya's AI pipeline uses system prompts at every stage (routing, staging, summarization, chat, etc.). All prompts ship with sensible defaults, but you can override any of them by placing files in ~/.laya/prompts/:
mkdir -p ~/.laya/prompts
# Override the router prompt (controls event classification)
vim ~/.laya/prompts/router.md
# Override a worker persona
vim ~/.laya/prompts/engineer.md
# Reload without restarting
curl -X POST http://127.0.0.1:8420/prompts/reload
Available prompt files: router.md, stager.md, omni.md, group_summary_initial.md, group_summary_rolling.md, briefing.md, summarizer.md, summarizer_status_change.md, engineer.md, comms.md, sales.md, hr.md, ops.md, finance.md, chat.md, chat_title.md, chat_polish.md, learner.md, context_learner.md, trace_narrative.md, trace_summary.md, trace_filter.md.
Custom prompts fully replace the built-in default for that stage. If a file is deleted, the hardcoded default is used automatically. The engine never creates or modifies files in this directory. Use GET /prompts to check which prompts are currently overridden.
| Store | Location | Purpose |
|---|---|---|
| SQLite | ~/.laya/data/laya.db | Events, cards, workspaces, spaces, traces, egress, chat |
| ChromaDB | ~/.laya/data/chroma/ | Vector embeddings for semantic search |
| n8n | ~/.laya/n8n/ | Workflow data, credentials (encrypted) |
| Logs | ~/.laya/logs/ | engine.log — rotating engine logs (10 MB × 5 files), verbosity set by the log level above. Also engine-stdout.log and n8n.log — captured process output, likewise rotated (10 MB × 3 files). |
Laya bundles the Python engine source into the Tauri app. On first launch, the app creates a Python virtual environment at ~/.laya/venv/ and installs dependencies automatically -- no Python installation is required on the end user's machine beyond what the app manages.
scripts/build.sh
This does two things:
engine/laya/, requirements.txt, requirements-ml.txt, and n8n/workflows/ into ui/src-tauri/resources/engine/scripts/build.sh # Build for current platform
scripts/build.sh --target x86_64-apple-darwin # Cross-compile for Intel Mac
scripts/build.sh --universal # Universal binary (arm64 + x86_64)
scripts/build.sh --sign "Developer ID App: ..." # macOS code signing
scripts/build.sh --skip-engine # Skip engine bundling (reuse previous)
| Platform | Format | Path |
|---|---|---|
| macOS | .app | ui/src-tauri/target/release/bundle/macos/Laya.app |
| macOS | .dmg | ui/src-tauri/target/release/bundle/dmg/Laya_0.1.0_<arch>.dmg |
| Windows | .msi | ui/src-tauri/target/release/bundle/msi/ |
| Windows | .exe | ui/src-tauri/target/release/bundle/nsis/ |
| Linux | .deb | ui/src-tauri/target/release/bundle/deb/ |
| Linux | AppImage | ui/src-tauri/target/release/bundle/appimage/ |
Note: macOS builds are unsigned by default. Unsigned apps trigger Gatekeeper -- users must right-click > Open to bypass. Pass
--signwith an Apple Developer identity to produce a signed build.
Architecture and design documents in docs/:
Deeper design docs (egress, AI processing rules, OAuth app distribution, pipeline lifecycle) live in engine/docs/.
380 commits
Python
53.7%
Svelte
29.4%
TypeScript
5.7%
HTML
4.5%
CSS
3.5%
Rust
3.0%
Laya is an open-source, local-first AI notification command center that aggregates Slack, Gmail, GitHub, Jira, Notion, Outlook, Calendar (and more) notifications using local LLMs via Ollama and LM Studio. Supports cloud models via BYOK.
Python
253
380 commits
updated Sep 24, 2026

A cadence for professional orchestration.
Laya is an open-source, local-first AI notification command center that aggregates Slack, Gmail, GitHub, Jira, Notion, Outlook, and Calendar notifications — powered by local LLMs via Ollama and LM Studio, or cloud models like Claude and GPT with your own API keys. It intercepts events from your professional tools, performs autonomous research and action-staging using LLM-powered agents, and presents you with ready-to-approve Action Cards -- so the answer is ready before you open the notification.
Works with:
Aggregates:
Your Tools (Jira, Slack, Gmail, Bitbucket, Calendar)
|
v
n8n (local Node.js) -- normalizes events
|
v
Laya Engine (Python) -- classifies, researches, stages
|
v
Laya UI (Tauri + Svelte) -- Action Cards you approve or dismiss
|
v
n8n -- executes approved actions (creates PRs, sends replies, etc.)
agent/<id>/<model> (Claude Code, Codex, Gemini, or Pi) for any stage. Claude Code enforces JSON schemas natively; other agents use best-effort schema + retrycards_fts/events_fts), merged via Reciprocal Rank Fusion, so both semantic and exact-keyword matches surfacehost field (empty or the cloud domain ⇒ cloud)| Layer | Technology |
|---|---|
| Desktop Shell | Tauri v2 (Rust) |
| Frontend | Svelte 5 (runes) + Skeleton UI + Tailwind CSS v4 |
| Backend | Python 3.10+ / FastAPI / asyncio |
| LLM Interface | LiteLLM (supports Anthropic, OpenAI, Google, Ollama) |
| Integration Gateway | n8n (local Node.js on port 45678) |
| Structured Storage | SQLite (async via aiosqlite, WAL mode) |
| Vector Storage | ChromaDB (embedded PersistentClient) |
| Embeddings | ONNX (built-in to ChromaDB) or sentence-transformers (optional) |
| Coding Agents | Claude Code / Gemini CLI / OpenAI Codex CLI / Pi CLI (usable as workspace agents and as inference backends) |
laya/
├── engine/ # Python FastAPI backend
│ ├── laya/
│ │ ├── main.py # Entry point (uvicorn server on :8420)
│ │ ├── config.py # Settings, paths, agent detection
│ │ ├── api/ # REST + WebSocket endpoints (27 routers)
│ │ ├── db/ # SQLite (+ FTS5) + ChromaDB + 70 migrations
│ │ ├── pipeline/ # Event processing (ingest → route → stage → emit → trace → learn → context_learn → omni)
│ │ ├── llm/ # LiteLLM client, agent inference backends, prompts, tools
│ │ ├── agents/ # Coding agent adapters (Claude, Gemini, Codex, Pi)
│ │ ├── workers/ # Multi-persona LLM workers (engineer, comms, ops, sales, hr, finance)
│ │ ├── egress/ # Outbound action execution (9 platforms)
│ │ ├── integrations/ # n8n bootstrap & client
│ │ └── security/ # OS keychain integration
│ ├── requirements.txt # Core Python dependencies
│ └── requirements-ml.txt # Optional: torch + sentence-transformers
│
├── ui/ # SvelteKit + Tauri desktop app
│ ├── src/ # Svelte 5 frontend (runes syntax)
│ │ ├── routes/ # Pages (feed, coherence, dashboard, settings, workspace, omni)
│ │ ├── lib/ # Components, API client, stores
│ │ ├── app.css # Tailwind v4 + theme system
│ │ └── app.html
│ ├── src-tauri/ # Rust/Tauri shell
│ │ ├── src/
│ │ │ ├── lib.rs # Tauri setup, commands, health polling, tray
│ │ │ ├── sidecar.rs # Python venv lifecycle & engine spawning
│ │ │ └── n8n.rs # n8n process management
│ │ ├── tauri.conf.json # Tauri config (resources, icons, window)
│ │ └── resources/ # Bundled engine source (production builds)
│ ├── package.json
│ └── svelte.config.js # Static adapter (SPA mode)
│
├── n8n/
│ └── workflows/ # Integration workflows (JSON, ~21 files: ingestion + executor per platform)
│
├── scripts/
│ ├── setup-dev.sh # One-time dev environment setup
│ ├── dev.sh # Start engine + Tauri dev server
│ ├── build.sh # Production build
│ └── update_icons.sh # Icon generation
│
├── landing/ # Landing page
└── docs/ # Architecture & design documents
The fastest way to try Laya is a prebuilt release — no toolchains required.
Open the Releases page and download the installer for your platform:
| Platform | Download |
|---|---|
| macOS | .dmg (universal — Apple Silicon + Intel) |
| Windows | .msi or .exe |
| Linux | .deb or .AppImage |
Install and launch. You do not need Python, Node, or Rust installed to run a release build. On first run, Laya checks for a compatible Python (3.10+) and Node.js (20+) already on your machine and uses those if found; otherwise it provisions its own bundled runtimes. Either way, a local n8n instance is set up under ~/.laya/.
Add an API key (Anthropic, OpenAI, Google, …) or point Laya at a local Ollama / LM Studio endpoint, then connect your tools from Settings.
macOS: release builds are signed, so they open normally — just double-click to launch.
Want to build from source, hack on the engine, or contribute? Follow the Development setup below.
You need three runtimes installed. Here's how to get each one:
brew install python@3.12 (or download from python.org)sudo apt install python3 python3-venv python3-pipVerify: python3 --version
brew install nodecurl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - && sudo apt install -y nodejsVerify: node --version && npm --version
Install via rustup:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Verify: cargo --version
macOS:
xcode-select --install
Linux (Ubuntu/Debian):
Tauri v2 requires system libraries for GTK, WebKit, and app-indicator support:
sudo apt install -y libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev patchelf
The default Linux inotify file watcher limit (65,536) can be too low for this project -- Vite needs to watch the source files while the Rust target/ directory consumes most of the quota, causing Tailwind CSS to silently fail to generate utility classes. Increase the limit:
# Immediate (resets on reboot)
echo 524288 | sudo tee /proc/sys/fs/inotify/max_user_watches
# Permanent
echo 'fs.inotify.max_user_watches=524288' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
scripts/setup-dev.sh
This script does the following:
python3, node, npm, and cargo are availableengine/.venv/ and installs dependencies from engine/requirements.txtui/node_modules/)~/.laya/n8n_module/~/.laya/data/ and ~/.laya/logs/scripts/dev.sh
This starts two processes:
python -m laya.main (with hot reload) at http://127.0.0.1:8420npx @tauri-apps/cli dev which starts Vite at http://localhost:5173 and opens the Tauri windown8n is managed automatically by the Tauri app -- it starts on launch (port 45678) and stops on quit.
Note: If the engine fails with "Address already in use", a stale engine process may be holding port 8420. The engine will attempt to kill it automatically on startup.
On first launch, the engine creates config files in ~/.laya/:
| File | Purpose |
|---|---|
settings.json | Models, agent paths, privacy settings, pipeline params |
team.json | Team member context |
rules.json | Event filtering rules |
repos.json | Git repository paths and metadata |
API keys (Anthropic, OpenAI, Google, etc.) are stored securely in your OS keychain and can be configured through the Settings UI.
The engine logs at INFO by default. Change verbosity from Settings → Data → Engine Log Level (DEBUG / INFO / WARNING / ERROR) — this maps to the logging.level key in settings.json and applies immediately, no restart. Set it to WARNING to record only warnings and errors and keep logs small. For a single run you can override it with the LAYA_LOG_LEVEL environment variable, which takes precedence over the setting (and also sets uvicorn's request-log level).
Laya's AI pipeline uses system prompts at every stage (routing, staging, summarization, chat, etc.). All prompts ship with sensible defaults, but you can override any of them by placing files in ~/.laya/prompts/:
mkdir -p ~/.laya/prompts
# Override the router prompt (controls event classification)
vim ~/.laya/prompts/router.md
# Override a worker persona
vim ~/.laya/prompts/engineer.md
# Reload without restarting
curl -X POST http://127.0.0.1:8420/prompts/reload
Available prompt files: router.md, stager.md, omni.md, group_summary_initial.md, group_summary_rolling.md, briefing.md, summarizer.md, summarizer_status_change.md, engineer.md, comms.md, sales.md, hr.md, ops.md, finance.md, chat.md, chat_title.md, chat_polish.md, learner.md, context_learner.md, trace_narrative.md, trace_summary.md, trace_filter.md.
Custom prompts fully replace the built-in default for that stage. If a file is deleted, the hardcoded default is used automatically. The engine never creates or modifies files in this directory. Use GET /prompts to check which prompts are currently overridden.
| Store | Location | Purpose |
|---|---|---|
| SQLite | ~/.laya/data/laya.db | Events, cards, workspaces, spaces, traces, egress, chat |
| ChromaDB | ~/.laya/data/chroma/ | Vector embeddings for semantic search |
| n8n | ~/.laya/n8n/ | Workflow data, credentials (encrypted) |
| Logs | ~/.laya/logs/ | engine.log — rotating engine logs (10 MB × 5 files), verbosity set by the log level above. Also engine-stdout.log and n8n.log — captured process output, likewise rotated (10 MB × 3 files). |
Laya bundles the Python engine source into the Tauri app. On first launch, the app creates a Python virtual environment at ~/.laya/venv/ and installs dependencies automatically -- no Python installation is required on the end user's machine beyond what the app manages.
scripts/build.sh
This does two things:
engine/laya/, requirements.txt, requirements-ml.txt, and n8n/workflows/ into ui/src-tauri/resources/engine/scripts/build.sh # Build for current platform
scripts/build.sh --target x86_64-apple-darwin # Cross-compile for Intel Mac
scripts/build.sh --universal # Universal binary (arm64 + x86_64)
scripts/build.sh --sign "Developer ID App: ..." # macOS code signing
scripts/build.sh --skip-engine # Skip engine bundling (reuse previous)
| Platform | Format | Path |
|---|---|---|
| macOS | .app | ui/src-tauri/target/release/bundle/macos/Laya.app |
| macOS | .dmg | ui/src-tauri/target/release/bundle/dmg/Laya_0.1.0_<arch>.dmg |
| Windows | .msi | ui/src-tauri/target/release/bundle/msi/ |
| Windows | .exe | ui/src-tauri/target/release/bundle/nsis/ |
| Linux | .deb | ui/src-tauri/target/release/bundle/deb/ |
| Linux | AppImage | ui/src-tauri/target/release/bundle/appimage/ |
Note: macOS builds are unsigned by default. Unsigned apps trigger Gatekeeper -- users must right-click > Open to bypass. Pass
--signwith an Apple Developer identity to produce a signed build.
Architecture and design documents in docs/:
Deeper design docs (egress, AI processing rules, OAuth app distribution, pipeline lifecycle) live in engine/docs/.
380 commits
Python
53.7%
Svelte
29.4%
TypeScript
5.7%
HTML
4.5%
CSS
3.5%
Rust
3.0%