{
  "markdown": "# Cortland 🍎\n\n**Give an AI real access to your Mac — Mail, Calendar, Reminders, Notes, files —\nwithout giving it the keys.** Every consequential action previews first, waits\nfor your approval through a channel the model can't touch, gets logged, and can\nbe undone. Bring your own model: Claude, or a local one that never leaves the\nmachine.\n\n```\nYou:       Add a reminder to call the vet tomorrow at 2pm\nCortland: Received — working on it…\nCortland: Added \"Call vet\" for tomorrow, Aug 3 at 2:00 PM.\n```\n\nThat conversation happened over iMessage, answered by a 4 GB model running on\nan 8 GB MacBook Air. Nothing left the Mac.\n\nThe texting interface needs a **second Apple ID**, signed into Messages on the\nMac only — a mouthpiece, not a worker. It owns no mail, no calendar, no data.\nYou text it from your phone like any contact; the Mac still does the work as\n*you*. Your system iCloud stays yours. Claude Code, Cursor, Codex, and other\nMCP clients skip this step: they talk to the same tools over stdio.\n\n---\n\n## What it looks like in use\n\n**Ask about your week** — reads are free, no approval needed:\n\n```\nYou:       Anything from the school this week?\nCortland: Three emails from Lincoln Elementary. Two are the weekly\n            newsletter; one from Ms. Alvarez on Tuesday asks for a\n            permission slip by Friday.\n```\n\n**Ask it to change something** — writes stop and ask, every time:\n\n<p align=\"center\">\n  <img src=\"docs/images/imessage-approval.svg\" alt=\"An iMessage thread: the user asks to delete a reminder; Cortland acknowledges, then replies with an approval request naming the reminder, its list, and its due date, asking the user to reply &quot;yes a3f9c1&quot;; the user replies with the code; Cortland confirms the deletion with the same details.\" width=\"430\">\n</p>\n\nThe code in that prompt is generated by the framework and never enters the\nmodel's context — so even a fully prompt-injected model can't forge your\napproval. Ignore the message and the action expires, refused.\n\n**Ask what it can do** — answered from the tools actually installed, so it\ncan't overclaim:\n\n```\nYou:       what can you do?\nCortland: Here's what I can do with your Mac:\n            • Calendar: list, look up, create, delete\n            • Mail: search, read, follow threads, draft, send (asks first), flag, file\n            • Notes: search, read, create, add to\n            • Reminders: list, search, create, tick off, delete\n\n            Anything that deletes or changes things asks you first — I text\n            you a code and wait for \"yes <code>\". Reads just happen.\n```\n\n**In Claude Code or Claude Desktop**, the same tools are just there:\n\n> **You:** send a reply to Ms. Alvarez saying the slip is coming Thursday\n> **Claude:** *[searches mail, reads the thread]* I can send this from your\n> Personal account to alex@example.com. Approve in the usual channel.\n\n`mail_send` is write-gated. Live mode plus per-action human approval. A stolen\ntoken can only *ask*.\n\n---\n\n## Requirements\n\nmacOS only. Nothing here runs on Windows or Linux — the tools talk to Apple\napps via Automation.\n\n| | Minimum | Comfortable |\n|---|---|---|\n| **OS** | macOS 13 Ventura | macOS 15+ |\n| **Mac** | Any Mac that runs Ventura (Intel can drive Cursor/Claude) | Apple Silicon (M1 or newer) |\n| **Node** | 20 LTS (`node -v`) | 22 |\n| **Toolchain** | Xcode Command Line Tools (`xcode-select -p`) | same — `better-sqlite3` compiles native code |\n| **Client** | One MCP app: Cursor, Claude Code, Claude Desktop, Codex, or LM Studio | plus Ollama if you want to **text** it |\n| **RAM** | 8 GB (cloud/Cursor model, or Gemma 4 E2B) | 16 GB+ for larger local models |\n| **Disk** | ~500 MB for this repo’s build | **+6 GB** if you pull `gemma4:e2b-it-qat` |\n| **Apple apps** | The apps you want touched, signed in as you | Mail + Reminders is the usual first pair |\n\n**iMessage bridge only:** macOS 13+ (modern `chat.db`), Full Disk Access for\nthe process that runs Node, Automation → Messages, and a **second Apple ID**\nsigned into Messages.app — not into System Settings. See\n[SETUP.md §5.1](SETUP.md#51-create-the-assistants-apple-id).\n\n**Osaurus as a client:** Apple Silicon, macOS 15.5+.\n\nNot required: an npm `@cortland` install (0.2.0 is GitHub-only for now), a\ndeveloper account, iCloud for Path A, or any API key.\n\n---\n\n## Quick start\n\n**Fastest path to “it searched my mail”** — Cursor (or Claude Code) as the\nbrain, no second Apple ID, no Ollama:\n\n```bash\nxcode-select --install          # if `xcode-select -p` fails\ngit clone https://github.com/cortland-suite/cortland.git\ncd cortland\nnpm install && npm run build    # first install compiles sqlite — a minute\n```\n\nThen Cursor Settings → MCP, add servers pointing `node` at\n`packages/mail/dist/server.js` (and reminders/notes/calendar if you want\nthem). Absolute paths. JSON is in\n**[docs/08_local_models.md](docs/08_local_models.md#cursor)**.\n\nAsk: *search my mail for anything from school this week.* macOS will prompt\n**Automation** (Cursor → Mail). Allow it.\n\nWrites stay previews until you set `\"live\": true` in\n`~/Library/Application Support/cortland/config.json`. Even then, send/delete\nasks you per action.\n\n**Texting it from your phone** is a longer path (second Apple ID + Ollama).\n[SETUP.md](SETUP.md) is the ordered list; [docs/08](docs/08_local_models.md)\nis the illustrated one.\n\n---\n\n## Why it exists\n\nMost MCP servers for personal data are thin wrappers around AppleScript:\n`delete_email`, `send_message`, executed the instant a model calls them. That's\na hard thing to trust with an inbox, and the alternatives don't help — cloud\nassistants (Poke, Arlo, Lindy) can't touch Apple-native data at all, because\nApple gives them no API, and self-hosted agents like OpenClaw run ungoverned\n(a CVSS 8.8 in January 2026, with permission gates still on the roadmap).\n\nCortland inverts the default. A tool call *previews* what it would do unless\nyou've opted into live mode, and even then every consequential action waits for\nyou. The framework enforcing that is a small library with a test suite proving\neach guarantee can't be bypassed — and every tool in the suite is built on it.\n\n---\n\n## The guarantees\n\n| | |\n|---|---|\n| **Dry-run by default** | Gated tools preview instead of executing. Every config error resolves *toward* dry-run. |\n| **The human gate is out-of-band** | Approval arrives via a native dialog, a file you move, your client's own UI, or a text you reply to — never through model text. |\n| **Everything is audited** | Success, failure, dry-run, denial, refusal: one local SQLite row each. \"What did my tools actually do?\" always has an answer. |\n| **Undo is enforced at registration** | A tool claiming native undo must produce a recipe *before* the write, or the framework refuses it. |\n| **Content is data, not instructions** | Everything read from mail, notes, or messages returns inside a nonce-delimited fence. |\n| **Safety by absence** | No ungated send. No attendee invitations. No reading conversations other than your own. |\n| **Local-first** | No accounts, no credentials, no cloud. Your model, your machine, your disk. |\n\n---\n\n## Install\n\n`@cortland` 0.2.0 is not on npm yet. From the repo:\n\n```bash\ngit clone https://github.com/cortland-suite/cortland.git\ncd cortland\nnpm install\nnpm run build\nnpx cortland setup\n```\n\nThe wizard asks before every step and records what it did. Full walkthrough —\nincluding the iMessage bridge, permissions, and model choice — in\n**[SETUP.md](SETUP.md)**. Connecting Cursor, Codex, LM Studio, Osaurus, or\nOllama: **[docs/08_local_models.md](docs/08_local_models.md)**.\n\n---\n\n## Packages\n\n| Package | What it does |\n|---|---|\n| [`@cortland/governed`](packages/governed) | The framework: dry-run defaults, approval gates, audit, provenance, undo, injection fencing. Build your own governed tools on it. |\n| [`@cortland/mail`](packages/mail) | Apple Mail: read, search (two tiers), threads, drafts, send (write-gated). |\n| [`@cortland/reminders`](packages/reminders) | Reminders: lists, search, create, complete, delete — with native undo. |\n| [`@cortland/notes`](packages/notes) | Notes: folders, search, read, create, append. |\n| [`@cortland/calendar`](packages/calendar) | Calendar: window queries, create, delete. Cannot send invitations, by design. |\n| [`@cortland/context`](packages/context) | Local context layer: mail/calendar *metadata* (pointers, never bodies), briefings, person lookups, a corrections flywheel. |\n| [`@cortland/imessage`](packages/imessage) | Text your own AI. Second Apple ID in Messages (mouthpiece only); owner-only by construction, approvals by reply. |\n| [`@cortland/folders`](packages/folders) | Folder-as-API: drop a file in iCloud from any device, a declared local pipeline runs. |\n| [`@cortland/remote`](packages/remote) | Reach the suite from your other devices over your own private network. |\n| [`@cortland/setup`](packages/setup) | The onboarding wizard. |\n\n---\n\n## Bring your own model\n\nCortland is the tools. You pick the brain.\n\n<p align=\"center\">\n  <img src=\"docs/images/two-paths.svg\" alt=\"Path A: an MCP client hosts the model and Cortland is a plugin. Path B: you text an iMessage bridge; Ollama is the brain; the Mac still acts as you.\" width=\"860\">\n</p>\n\n**Path A.** Cursor, Claude Code, Claude Desktop, Codex, LM Studio, or Osaurus\nhosts the model. Cortland is an MCP plugin. No second Apple ID.\n\n**Path B.** You text it. A second Apple ID signs into Messages on the Mac\n(mouthpiece only). Ollama runs Gemma 4 on disk. Approvals are `yes <code>`\nin the same thread.\n\nField-tested on an 8 GB M2: `gemma4:e2b-it-qat` (4.3 GB) makes clean tool\ncalls and refuses honestly. The governed contract matters *more* with a\nsmall model, not less.\n\n**Walkthroughs, including LM Studio and Osaurus from a clean install:**\n**[docs/08_local_models.md](docs/08_local_models.md)**. Clean-Mac order,\npermissions, and the iMessage second-ID steps: **[SETUP.md](SETUP.md)**.\n\n---\n\n## Privacy model\n\n- **Pointers, not copies.** The context layer stores metadata referencing\n  messages by ID; bodies are never stored. Delete a message in Mail and the\n  pointer dangles and gets pruned.\n- **Model use is opt-in and declared.** Works fully deterministically with no\n  model. Configure one and its network egress is declared in config and\n  recorded in the audit log on every run.\n- **Nothing phones home.** The only optional outbound call is a push ping you\n  configure yourself, and its body is a fixed string carrying no information.\n\n---\n\n## Docs\n\n- **[SETUP.md](SETUP.md)** — clean Mac to working assistant, with the gotchas.\n- **[docs/08_local_models.md](docs/08_local_models.md)** — connect Cursor, Codex,\n  LM Studio, Osaurus, or Ollama; illustrated, from zero.\n- **[SECURITY.md](SECURITY.md)** — reporting, and what's in scope.\n- **`docs/`** — one design doc per component: the framework contract (01),\n  Mail (02), folder-as-API (03), the context layer (04), remote access (05),\n  the iMessage bridge (06), the threat model / review guide (07), and local\n  models (08).\n- **`NOTES.md`** — the engineering log: decisions with dates, open questions,\n  and every field finding, including the ones that were embarrassing.\n\n## License\n\nMIT. See [LICENSE](LICENSE).\n",
  "bytes": 11207,
  "sha": "46713a2d580a8a4d64daa6034f89532efea3ee3ad65290141bba8d5c186f6f85",
  "repo_slug": "honeycrisp-suite/honeycrisp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_honeycrisp_suite_context_746a9b79/readme"
}