Jul 2, 2026

The filesystem is the agent interface

How Locality mounts apps as local files, and why the hard part is pushing edits back safely

Agents are good at files. They read them, grep them, edit them, create them, and recover from a bad command without much help. That skill was learned on code, but it transfers to anything that looks like a file.

Agents are much worse at bespoke application APIs. Every SaaS product has its own object model, its own auth, its own idea of what an "update" means. Teaching an agent to work in Notion, then Linear, then Google Docs means teaching it a new tool surface each time, and paying for that surface in context on every call.

Your company's operating knowledge mostly lives in those apps. So the agent that is fluent in files ends up working through a narrow, app-specific straw to reach the documents it actually needs.

Locality closes that gap. It mounts Notion as a folder of local Markdown files, so an agent can work with your knowledge base using the tools it already understands. The reading half of that is straightforward. The interesting engineering is in the other direction: letting an agent edit those files freely, then writing the changes back to Notion without corrupting your source of truth.

The simple idea

Point Locality at a Notion workspace and it appears on your machine as ordinary files:

  • Pages become directories, with the page body in page.md.

  • Child pages become child directories.

  • Databases become directories, and rows become page-like directories inside them.

  • A database schema is rendered as _schema.yaml.

  • Media is downloaded locally and referenced from the Markdown.

Open a file and you get normal Markdown with a small block of frontmatter that carries the page's Notion identity.

Reading is the easy half

If Locality stopped at "Notion as read-only Markdown," it would be a decent export tool and not much more. The reason it is a sync engine instead is that edits have to flow back.

Writing back is where the difficulty lives, because a local Markdown file and a Notion page are not the same shape. Notion is a tree of typed blocks with stable IDs. Markdown is text. Turning "the user changed this paragraph" into the correct, minimal set of Notion API operations, without losing block identity or clobbering a change someone else made upstream, is the actual product.

We treat a local edit as a source-aware diff against a known baseline, never as a blind overwrite of the whole page.

Three trees, one merge base

Locality tracks three states for every entity:

  • The local tree: the current state of your files.

  • The remote tree: the latest Notion state Locality has observed.

  • The synced tree: the last state both sides agreed on, stored as a shadow.

Every sync decision falls out of comparing those three:

  • Local matches synced, remote matches synced: clean, nothing to do.

  • Local matches synced, remote differs: Notion changed, a pull is safe.

  • Local differs, remote matches synced: you have a pending local edit, a push is possible after review.

  • Local differs and remote differs: divergence, which means merge, conflict, or stop for review.

If that sounds like git, that is the right instinct. The synced shadow is a merge base. Paths and Markdown are just a human-editable projection on top. The remote IDs and shadows are the real identity, and they are what protect correctness when a filename or a heading changes. This is closer to git's merge model plus Dropbox-style sync discipline than to a one-shot import and export.

Block-aware diffs, not whole-page replacement

When you run loc diff, Locality parses the edited Markdown, compares it block by block against the synced shadow, and produces a connector-neutral push plan: update this block, append that one, replace this block's type, archive this deleted block, update these page properties.

Execution then maps that plan to the smallest safe sequence of Notion API calls that reproduces your intended change while preserving block identity. If a paragraph's text changed, Locality updates that block in place. If you added a block, it appends the matching Notion block in the right position. If you changed a block into a type the API cannot mutate in place, it creates the replacement and archives the old one after journaling the new ID.

Block alignment is why this matters. A good alignment preserves existing Notion block IDs and produces a handful of targeted calls. A poor alignment degrades into recreating a run of blocks, which is slower and more visible in the page history. The planner minimizes calls where it safely can, but correctness beats cleverness: if Locality cannot prove which local block maps to which remote block, it uses a conservative plan or asks for review rather than risk corrupting the page.

Some Notion blocks cannot yet be round-tripped safely. Rather than silently mangle them, Locality renders them as anchored directives:

::locality{id=... type=synced_block title="Shared header"}
::locality{id=... type=synced_block title="Shared header"}
::locality{id=... type=synced_block title="Shared header"}

The rule here is deliberately strict. An agent may move a directive line unchanged, or delete it where deletion is supported, but editing a directive's internals is rejected before push. Unsupported content stays visible and honest instead of pretending every block type is safe to rewrite.

The push is where safety is enforced

The design goal is that an agent can edit locally with full freedom, while remote writes stay controlled. Local edits never touch Notion until you push. When you do, the pipeline is built around recovery rather than magic:

  • Validation runs before planning: frontmatter identity, directive integrity, schema-backed database properties, conflict markers, and unsupported write shapes are all checked.

  • The plan is journaled before any remote mutation, so there is always a record of intended and applied effects.

  • Remote freshness is re-checked immediately before apply. Background sync is a helpful hint, but the push preflight is authoritative. A stale hint never leads to a blind overwrite.

  • Connector operations carry deterministic operation IDs, and their effects are journaled.

  • After apply, Locality reads the page back from Notion and reconciles local shadows to the new truth.

  • Undo is derived from journaled preimages and operation effects where the API supports it.

  • Dangerous plans, like large deletes or moves, require explicit confirmation.

  • If both sides changed, Locality either handles disjoint changes or writes inline conflict markers and refuses to push until they are resolved.

The short version: Locality does not ask you to hand an agent raw write access to Notion. The agent works locally, and a reviewable sync engine translates safe edits back.

Local-first, and no tokens in the agent's hands

Content lives on your machine and goes directly to the Notion API. There is no Locality server sitting in the middle of your data path in the current design.

Auth follows from that. loc connect notion prefers OAuth through a broker, so the local client never ships or stores the Notion OAuth client secret. Access and refresh credentials live in your OS credential store. The local state database holds metadata and secret references, not bearer tokens, and daemon IPC does not carry tokens either. Mounts reference a connected account, and credentials are resolved only at the moment a pull or push executes. Read-only mounts exist for research workflows where you never want a write path at all.

Agents are also told, in the guidance Locality installs, to treat mounted Notion content as untrusted remote data. It is text from the outside world, not instructions to follow.

The mount teaches the agent how to behave

Locality installs guidance where agents already look: AGENTS.md and CLAUDE.md in the mount, Codex and Claude skill and config files, Warp and OpenCode guidance, and compatible fallbacks for Cursor, Windsurf, and others.

That guidance teaches the filesystem contract. Edit page.md. Preserve the Locality frontmatter and directives. Create a child page as a directory with its own page.md. Inspect pending work with loc status, review with loc diff, and only push when the user asks. This is part of the product, not documentation garnish. The mounted folder itself is what makes the first agent interaction follow the rules.

What is real today

Locality runs as a local Rust system: a CLI for humans and agents, a background daemon that owns sync state and serializes mutating work through a single queue so pulls, pushes, hydration, and watcher events do not race, a connector-agnostic core that owns the sync model and safety, and a first-party Notion connector.

For supported shapes, the full loop is implemented and exercised against live Notion: read, pull, render, hydrate, narrow writes, media handling, database schema rendering, row creation, property edits, journaling, reconciliation, and undo. Unsupported or layout-heavy blocks are preserved through directives until safe write support exists. macOS is the lead platform, presenting the mount under ~/Library/CloudStorage/Locality/notion through File Provider, with Linux via FUSE and Windows via Cloud Files sharing the same daemon boundary. The OS layers do presentation, not policy.

We are being deliberate about what we claim. Desktop onboarding and the review UI exist, with production polish still in progress, and some OS-provider coverage is still manual depending on platform. We would rather ship a narrow, honest write path than a broad one that occasionally corrupts a page.

Where this goes

Notion is the first connector, not the last. The core that owns sync classification, validation, diff planning, conflicts, journaling, and guardrails is connector-agnostic by design, so the same engine extends to other systems of record as their connectors mature. Correctness lives in the core; each connector only has to know its own API.

The larger bet is that the filesystem is the narrow waist for agent tooling. Actions-based interfaces like MCP are useful for calling operations, but agents are strongest when they can inspect, search, edit, and diff in a durable workspace. Locality gives them that workspace and takes on the unglamorous part: identity, freshness, validation, push planning, and recovery.

Try it

Locality is in beta and free during beta. Mount your Notion workspace, point your agent at the folder, and let it work with your docs the way it already works with code. Review the diff before anything changes upstream.

Mount Notion. Edit locally. Push deliberately.


Blog Image

Jul 2, 2026

The filesystem is the agent interface

How Locality mounts apps as local files, and why the hard part is pushing edits back safely