A platform where your personal AI agent represents you — talking to other people's agents to negotiate, coordinate, and settle the everyday back-and-forth so you don't have to. macOS desktop app.
You have a personal AI agent — a "clone" that knows your preferences, your calendar, and your constraints. When you need to arrange something with another person, you don't do the back-and-forth yourself: your clone talks to their clone.
The two agents work out the details between them — a meeting time, the terms of a deal, who does what by when — and each comes back to its owner with a proposal. Nothing is final until the human approves it.
Booking a meeting across two calendars is the first thing ENISHI does today. But the goal is much bigger: any coordination between two people that currently takes a dozen messages — business arrangements, negotiations, plans, favors — carried out by agents that represent each side faithfully, keep private things private, and never act on their own.
ENISHI is a macOS desktop app I'm building as a personal project. This repo is the working codebase; the detailed internal design spec is kept private.
The names. Enishi (縁) is the bond that forms between people. Aun (阿吽) comes from a-un no kokyū — the wordless, in-and-out breathing of two people who move in perfect sync. That is exactly what two delegated agents are meant to do: reach an understanding on their principals' behalf without either side having to spell everything out.
Coordination is expensive. Picking a meeting time across busy calendars, or settling small terms between two parties, eats real time and attention even though the decision itself is trivial. Handing that off to an AI assistant sounds obvious, but two problems make it hard to trust:
- Privacy. To negotiate a time, your agent has to reason over your calendar — but the other side should never see your raw schedule, only whether a proposed slot works.
- Control. An agent that can act for you is also an agent that can commit you to things you didn't want. There has to be a hard gate where a human signs off before anything real happens.
ENISHI is my attempt to build the plumbing for delegated agents that negotiate without leaking their principal's data and without acting without consent.
Each user runs a local node. Two nodes talk through a relay that only forwards messages — it never sees decrypted content or makes decisions.
User A's Mac User B's Mac
┌──────────────────┐ ┌──────────────────┐
│ ENISHI Desktop │ │ ENISHI Desktop │
│ (Tauri 2 + React)│ │ (Tauri 2 + React)│
│ │ │ │ │ │
│ random port + │ │ random port + │
│ random token │ │ random token │
│ ▼ │ │ ▼ │
│ Local Core │ │ Local Core │
│ (FastAPI, │ │ (FastAPI, │
│ 127.0.0.1 only)│ │ 127.0.0.1 only)│
│ └─ SQLite │ │ └─ SQLite │
└────────┬─────────┘ └─────────┬────────┘
│ │
└──────────► Relay ◄───────────┘
(forward-only: TTL,
destination authz,
size/rate limits,
minimal logging)
The desktop app (Tauri 2 + React + TypeScript) is just the UI. The real work happens in the Local Core, a FastAPI service that binds to 127.0.0.1 only. Tauri launches it as a child process with a random port and a random bearer token per session, and kills it on exit so no orphan process is left listening. Every /v1/* route requires that token.
Agents exchange structured messages, not free text. Each message has a typed message_type:
REQUEST → PROPOSE → COUNTER → ACCEPT / REJECT → REQUEST_APPROVAL → APPROVAL_RESULT → EXECUTE → RECEIPT, plus ERROR.
A negotiation is a state machine over these messages. Proposals and counter-proposals are exchanged as deltas rather than full state, so a round of haggling stays small. The schemas live in packages/protocol/ as JSON Schema, shared between both sides.
Three ideas do the heavy lifting:
- Clone agents. When you delegate a task, ENISHI spins up a scoped agent (a "clone"). It starts in
review_requiredstate and cannot perform any high-privilege action until you explicitly activate it. The default profile denies destructive operations outright. - Selective disclosure. You configure, per peer, what your agent is allowed to reveal. A negotiating agent answers "does this slot work?" without ever transmitting the underlying calendar. Memories marked
secretnever leave the device and are never packed into a clone's context. - Approval gate with expiry. Before anything is executed, the protocol routes a
REQUEST_APPROVALto the human. Approvals carry anexpires_at; once expired, the action can no longer run, so a stale "yes" from last week can't be replayed into an action today.
The threat model assumes the relay is untrusted and the other agent may be adversarial. Some of the concrete decisions:
- Local Core listens on
127.0.0.1only — binding to0.0.0.0is explicitly disallowed. - Bearer token comparison uses constant-time comparison (
secrets.compare_digest) to avoid timing leaks. - The app never builds shell command strings. External CLIs are invoked as a command name plus a validated argument array, never a concatenated string.
- CLI detection is limited to
shutil.whichplus a--versionprobe. ENISHI never reads another tool's credentials. - Secrets go to the macOS Keychain, never to SQLite or JSON on disk.
More detail is in docs/security.md.
Under active development. The core negotiation loop, node identity, selective disclosure, the clone lifecycle, the relay, and the human-approval gate are implemented and demoable across two local nodes plus a relay (see docs/demo.md). Remaining work is the native Rust/Tauri packaging (Keychain integration, code signing) and distribution.
Design notes in this repo:
docs/architecture.md— component layoutdocs/protocol.md— the AUN Protocoldocs/security.md— security posturedocs/clone-memory.md— clones and memory
apps/desktop/— Tauri 2 + React + TypeScript desktop appservices/local-core/— FastAPI Local Core (127.0.0.1 only, bearer auth required)services/relay/— forward-only relay serverpackages/protocol/— AUN Protocol message schemas (JSON Schema)scripts/— environment checks and dev helpers
Note: internal package and bundle identifiers still use the project's former codename (
twinlink_core, etc.). The public rename to ENISHI is in progress.
TypeScript · React · Tauri 2 · Python · FastAPI · SQLite
Requires macOS 13+, Node.js 20+, Python 3.12+ with uv, and Rust (for the Tauri build).
npm install # frontend deps
cd services/local-core && uv sync --group dev # Python deps
./scripts/check_macos_env.sh # verify toolchainRun the desktop app (Tauri generates a random port + token for the Local Core and tears it down on exit):
./scripts/dev_desktop.shRun the Local Core on its own for API development:
./scripts/dev_core.sh # http://127.0.0.1:8765
npm run dev # Vite dev server (http://localhost:5173)# Python
cd services/local-core && uv run --group dev pytest
uv run --group dev ruff check . && uv run --group dev mypy twinlink_core
# TypeScript
npm run test && npm run typecheck
# Rust
cd apps/desktop/src-tauri && cargo testBuilt by Nakamura Masashi. See also Stellise, an on-device AI alarm app on the App Store.