localoy.ai

build AI agents on your own machine
Open-source skills & memory · permissions enforced · free to start

Build your own AI agents. Stay in control.

An agent here is plain files you can read — localstack gives it working skills, localbrain gives it memory on your disk. Change a line, run it again: that's building. And it only gets the tools you granted — decided when the runtime starts, not promised in a settings page. Watch every step, answer what it cannot decide alone, revoke it mid-run.

✦ Download All platforms
macOS · Windows · Linux — free, from GitHub Releases.
· Four minutes, every frame a real run
recorded · real runs

What you just watched is the whole pitch. An agent hired from a text-file brief finds real leads with evidence, drafts outreach a human sends, remembers what it already shipped — and answers for it when asked. Below, the same app live with sample data.

01 · Set it up once — every control below works
demo · simulated data

This is the control room, and it stays one. Pick a model, grant an agent files, web, browser or shell — or don't — watch what it touches, read the transcript, revoke a chat. Those chips are not labels: they are the tool list the runtime is handed at start, which is the subject of the next section. The skills an agent deploys with are localstack, our open-source suite — every one readable before you grant it anything.

02 · AI safety — what an agent here can and cannot do

The thing you are actually deciding. An agent with your files, your logged-in browser and a chat app pointed at it is the most access you have ever handed a program that takes instructions from text — text that can arrive from a web page it read or a stranger in a group chat. Below is what stops that turning into a bad afternoon: nine mechanisms in the daemon, five checks you can run yourself in about a minute, and the two things we would rather you heard from us than found later.

capabilities

Permissions are an allowlist, not a label

The files · web · browser · shell chips are the tool list the runtime is started with. An agent without browser has no browser tool to call — there is nothing at run time left to refuse.

credentials

Keys go into the Keychain and don't come back

Provider keys are filed under the localoy service, encrypted at rest. The daemon reads one to launch an engine; no HTTP route returns it, so a stolen local token can't be traded for your API keys.

network

Nothing is listening

The daemon binds 127.0.0.1 only, behind a bearer token minted at startup into a 0600 file, plus a strict Origin allowlist so no website can drive it.

transport

Telegram is polled outward

Long polling, not webhooks: no public URL, no tunnel, no inbound port, nothing to forward from a laptop behind NAT. The connection is always one your machine opened.

senders

Only the people you list can drive it

The allowlist holds numeric transport ids, never @usernames — a handle can be released and re-registered by someone else. Anyone not on it is dropped, not answered.

injection

Everyone else in the room is data

Group chats are opt-in at link time, because every other member's text arrives as untrusted content in the agent's view: material to read, never instruction to follow.

notifications

The agent can't forge a message from itself

What reaches your phone is emitted by the daemon, not by a tool the model can call. “Your agent messaged you” is a fact about the run, not something a prompt can fabricate.

revocation

Unlinking works mid-flight

A binding is a capability checked when it is exercised, not when it was issued. Kill it in the app and a message already in flight stops working — no restart, no window where the old link still counts.

scope

The chat cannot choose where it works

The working folder is picked in the app when you link, stored on the binding, and inherited by every session that chat creates. There is no message you can send that moves an agent somewhere else on the disk.

Don't take our word for it — five checks, about a minute

Every one of these runs against the free build, on your own machine. If any of them behaves differently than described here, that is a bug and we want the report.

  1. The permission is the tool list. Deploy an agent with only files granted, then ask it to run a shell command. It does not refuse you politely — the transcript shows it never had a shell tool to call.
  2. Nothing is listening. lsof -nP -iTCP -sTCP:LISTEN | grep localoyd One socket, on 127.0.0.1. No 0.0.0.0, no second port, nothing a machine on your network can reach.
  3. The key is in the Keychain, not in a file. security find-generic-password -s localoy The item is there; the command prints its attributes and not the secret. Then grep the application directory for the key and find nothing.
  4. A stranger cannot drive your agent. Message your bot from a second Telegram account. Nothing happens — not a refusal, not a prompt to the agent, not a notification. Off the allowlist is dropped, silently, on purpose.
  5. Revoking is immediate. Unlink the chat in the app while a run is in flight, then send another message. The binding is checked when it is used, so the next message is already a stranger's.

Two things we would rather you heard from us.
1 · The Keychain ACL is not ours. Items are read through /usr/bin/security, so the ACL is granted to that binary rather than to localoy: against a process already running as you it is no stronger than a 0600 file. What it buys is encryption at rest, correct backup semantics, and no plaintext key in the application directory. Tightening it needs a signed bundle and the framework API, and it is on the list rather than in the release.
2 · A task-shaped prompt may leave the machine even with a local agent. The planning and verification calls that wrap a task run on your local model when one is running, and fall through to the same provider your agent already uses when one is not — your key, your provider, but that text leaves the machine. Start a local model and it stays on it. This is in our own tracker as a documentation bug; it is here because it is the kind of thing you would want to have been told.

✦ Try it yourself Free, unsigned builds, no account. The checks above work on the first run.
03 · Nothing new to install — the same agent, in Telegram

A chat app is not another surface to build — it is the one already open on your phone. No second app to install, no account to make, no push service of ours in the middle: your bot, your chat, polled by your own machine.

01

Link a chat to an agent

Generate a link code in the app, send /link YOUR-CODE to your bot. That chat is now bound to one agent, one session, one working folder — chosen on the Mac, and not changeable from the chat.

02

Ask for anything it has permission to do

Whatever the run costs — thirty-four fetches, a read, two files written — you get three messages: nothing for the first four seconds, one status line that edits itself, then the reply in plain text.

03

Approve what it can't decide alone

When the agent needs a human, the turn parks and the question appears as buttons in the chat and in the app at the same time. The first answer wins; every other surface shows how it was resolved.

The skills in that run are localstack — the open-source suite localoy deploys by default: a sales pipeline from /prospect-brief to /sales-retro that drafts outreach and never sends it — a human sends every message — plus the SEO stack. The same skills install into Claude Code, Codex and Hermes, and every one is readable at github.com/localoy-ai/localstack.

The Mac has to be awake. localoy holds no power assertion — a machine that sleeps pauses its agents, and a lid you closed is a machine asleep. Leave it running and walk away; what you send while it is out is delivered when it polls again.

Telegram is the transport that ships today. Authority — bindings, allowlists, approvals — lives above the transport, so the next one inherits it instead of inventing its own idea of who may speak.

From your phone · in Telegram — an app you already have

12:00
12
acme sales
bot
A
today
Message

A whole run, in four messages. Nothing for the first four seconds, one status line that edits itself however long the work takes, a permission you answer with a button, then the reply in plain text. No streaming, no narration — every line above is a format the daemon actually sends.

04 · Downloads — every platform, one release

macOS

Apple Silicon and Intel builds. The tray app plus the daemon, in one dmg. macOS 12+.

Windows

One-click NSIS installer, x64. Installs per-user — no admin prompt. Windows 10+.

Linux

AppImage runs anywhere; .deb for Debian and Ubuntu. x64 desktop — servers go headless below.

These builds are not signed. macOS blocks them on first open — “Apple cannot check it for malicious software” is that block, not a broken download. Three steps, once per install:

  1. Drag localoy to Applications. Opening it from the disk image will keep failing.
  2. Open it, and click Done on the block.
  3. System Settings → Privacy & Security, scroll down, click Open Anyway.

Windows. More info, then Run anyway.

Or clear the download flag yourself and skip all of it:

xattr -dr com.apple.quarantine /Applications/localoy.app

Latest build from main → Rebuilt on every change. Replaces a stable install in place.

05 · Any server, no app — the CLI is the whole runtime
headless install · linux & macos · amd64 & arm64
$ curl -fsSL https://localoy.ai/install.sh | sh
installed localoy and localoyd to ~/.local/bin
$ localoyd &
$ localoy doctor
daemon   up (version 1.7.7)

The desktop app is only a window onto the daemon — everything runs in localoyd, so a VPS gets the exact same runtime the desktop gets. Static Go binaries, no dependencies, no container required. The permission model travels with the agent, which is what makes where it runs — this Mac, a box you rent, a cloud — a capacity decision rather than a safety one.