Writing /Scry /2026-10-02

Shared Memory, Local Code

I wanted every agent and machine to remember the same projects without copying every repository index to a central server. Scry shares one memory authority and keeps code intelligence beside the code.

Written by
Read time
5 minutes
Topic
Scry

My agents run on more than one machine. The repositories do too.

That made Scry’s first memory deployment slightly awkward. Each machine could build its own memory graph from the transcripts available locally, but the graphs immediately diverged. A decision remembered on the laptop didn’t exist on the machine running the standing agent. Project aliases resolved differently. Both graphs were technically healthy and neither was authoritative.

Copying every Scry store between machines would have fixed the divergence and created several worse problems. I ended up sharing the memory graph while keeping code indexes local.

Repository indexes belong near repositories

Scry’s code, git, schema, HTTP, coverage, and repository graph stores are specific to a working copy and its environment.

The laptop may have branches that don’t exist on another machine. A local database schema may differ from staging. Captured HTTP traffic is temporary and often contains development-only data. File paths and index freshness are local facts.

Shipping those stores to a central daemon would introduce sync rules for data that can be rebuilt from source. It could also copy sensitive code and traffic away from the machine where they were produced.

So Scry keeps per-repository domains under a hashed path in ~/.scry/repos/:

code/index.db
git/index.db
schema/index.db
http/
graph/index.db
manifest.json

Each development machine indexes what it actually has. When a repository changes, its local watcher rebuilds the relevant index and swaps it into place.

Personal memory needs one current version

The memory graph represents projects, services, machines, decisions, and cross-session state. That knowledge should follow the person and agents, not a particular clone.

I run one authoritative memory graph on an always-on Mac mini. It ingests the agent sessions and durable facts that should survive across projects. Other machines query that authority.

I didn’t want the daemon listening on the network. Scry’s normal transport is JSON-RPC over a Unix socket, and I wanted to keep that property.

SSH supports StreamLocalForward, which forwards one Unix socket to another. A launchd job on the laptop keeps a local socket connected to the mini’s Scry socket. From the client’s perspective, memory is still a local socket path.

There is no public port or second RPC implementation. SSH handles the connection and access control I already use between the machines.

MCP profiles keep routing explicit

Scry exposes two useful MCP profiles for this arrangement.

The local profile advertises code, git, schema, HTTP, graph, and fleet-room tools. Those calls go to the daemon on the machine holding the repository.

The memory profile advertises four operations: recall, episodes, memory paths, and remember. The SCRY_MEMORY_SOCKET environment variable points those calls at the forwarded authority socket.

The agent sees one Scry vocabulary. Setup decides where the calls land.

This avoided a problem I had with the first four standalone tools. The agent had to decide whether a question belonged to Scry, Tome, Lore, or Flume. After unification, the domain still matters internally, but the user and agent don’t need four service names.

A memory authority isn’t a sync system

Only one daemon writes the shared memory graph. That is deliberate.

Multi-writer graph synchronization would need conflict resolution for entity aliases, fact invalidation, ingestion cursors, and concurrent extraction. None of that helps me answer what a project name means.

Remote machines send intentional memories to the authority or let their transcripts reach the authority’s ingestion process. They don’t merge independent BadgerDB stores.

If the authority is unavailable, local repository intelligence still works. Memory recall fails as its own domain instead of taking code search with it.

The tradeoff is that the shared graph depends on one machine and its backup story. That is acceptable for my setup. A team version would need real access control and replication, plus an answer to the much messier question of whose memory wins.

Repository references form the seam

Global entities carry repo_refs, paths to repositories they touch. The path is enough for an agent to move from a remembered project into the corresponding local graph.

For example, recall may return a project, the service that runs it, a deployment decision, and a repository path. The agent changes its working directory or targets that repository explicitly, then asks the local graph for code relationships.

This seam works across machines because the repository path is metadata, not a pointer into the memory store. A machine without that repository can still understand the project. It simply can’t answer local code questions about it.

Paths differ between machines, so aliases and canonical repository identities will eventually need more care. The current setup reflects my actual environment rather than solving a general distributed identity problem in advance.

What the split buys me

The mini and laptop now agree on project names and decisions while indexing their own working copies.

An agent on the laptop and a standing agent on the mini agree about project names and decisions. They don’t need identical clones, captured requests, or database connections. Losing the SSH forward removes memory temporarily but doesn’t make the coding tools unusable.

There are more sophisticated ways to build this, but I don’t need them yet. The current setup is one authority on the mini, one forwarded socket on the laptop, and no TCP listener for Scry.

Start a project conversation

Email Jeff

Write your note here, then open it in your email app. This site sends nothing: your email app opens with this draft and you press send there. No email app? Copy the address or the draft instead.

Open in your email app ↗

To jeff@hooton.codes · Closing this window discards the draft.