A distributed network of Feynmans churning on open research problems from Emergent Mind. Each pear is a peer in a room that joins problems, chats, and (later) gets assigned fragments of the research tree. Pears find each other over loopback and over a Tailscale tailnet (WireGuard). Looks like WebTorrent but instead of movies there are open problems.
Run every command below from the repository root. Needs Node.js ≥ 22.22 and pnpm; allow extra time for the first dependency install. No model API key, Python, or tmux is needed for a room.
Terminal 1 — start room lab and join the credit-assignment problem:
pnpm -C torrent install
pnpm -C torrent pear -- --room lab --auto-join credit-assignmentTerminal 2 — join the same room on this machine:
pnpm -C torrent pear -- --room lab --home .pears/lab/second --bridge 0 --auto-join credit-assignmentExpect the first pear to show coordinator, both to show one other peer, and
the credit-assignment problem to have one other participant. Press enter to
expand its subproblems. Keys: j/k move · space join/leave · m message ·
q quit (Ctrl-C also stops a pear).
On a second machine, install the dependencies and run Terminal 1's command
there instead. Both machines must be online on the same Tailscale/Headscale
network, use the same room name, and allow TCP ports 7100–7109 between them.
Check tailscale status if they do not connect. Add --local to keep a pear
on loopback; invite setup is below.
The first run writes your identity to ~/.feynman/identity.json: a keypair and
a device name drawn at random from the DESIGN.md list (Nonacris, Eridanus,
Corinth…). A second pear on the same machine needs its own with --home <dir>.
The second command disables its browser bridge so the first pear keeps port 7300.
Rooms currently share chat, membership and a problem graph. Joining a problem does not start research or donate CPU/GPU capacity. Automatic remote assignment and contribution-based compute permissions are not implemented.
Next: show the tree in a browser · run chunking and research · verify a run or the software · start several pears.
The research tree viewer displays paper arguments, dependencies, open questions, source passages and review verdicts. A fresh clone has no saved database: create a research run first. With a saved run:
node tools/research/cli.mjs view \
--db tools/outputs/credit-assignment.sqlite \
--out tools/outputs/credit-assignment.view.htmlOpen tools/outputs/credit-assignment.view.html in your browser (double-click
the file), or use the command for your desktop:
open tools/outputs/credit-assignment.view.html # macOS
xdg-open tools/outputs/credit-assignment.view.html # Linux desktopThe HTML is self-contained: viewing needs no server, API key or model call.
Click a node to inspect its source and review. Regenerate the HTML after changing
the database. Saved databases are ignored by git and are not included in a
fresh clone; create one with the next section first. If you have the earlier
credit-assignment-passages.sqlite run, substitute that path after --db.
For the live room graph as islands, leave Terminal 1 running and open a third terminal:
pnpm -C site install
pnpm -C site devOpen the URL Vite prints (normally http://localhost:5173). The game reads the
pear's bridge at http://127.0.0.1:7300; press M for the map. This shows the
room's seeded subproblems and peer updates. The research viewer and room graph
use separate databases; research runs are not automatically imported into rooms.
This executor turns one question into a source-backed tree. It needs an installed
Feynman runtime and configured provider credentials, in addition to Node.js.
torrent/.env.local is for pear agents; this executor uses Feynman's model
authentication. See runtime setup and options.
Check setup — replace the path with your Feynman app directory containing
package.json and node_modules:
export FEYNMAN_RUNTIME_ROOT=/absolute/path/to/feynman/app
node tools/research/cli.mjs doctordoctor lists configured providers and model IDs without making a model call.
Use a provider/model from that output in the command below.
Create a new tree — this retrieves papers and makes billable model calls; it can take several minutes:
node tools/research/cli.mjs run \
--db tools/outputs/credit-assignment.sqlite \
--provider openrouter --model anthropic/claude-haiku-4.5 \
--question-file graphs/corpus/open-problems-sept-11 --question-line 1 \
--task-file graphs/corpus/feynman-prompt.md \
--retrieval public --policy aco \
--max-sources 4 --max-model-calls 8 --max-tokens 80000 \
--max-input-chars 18000 --max-elapsed-ms 900000Change --question-line to select another problem. Use a new database path for
each new run. To continue an existing run with its saved configuration:
node tools/research/cli.mjs resume --db tools/outputs/credit-assignment.sqliteThe implemented chunking and scheduling steps are:
- Fetch paper text and retain its source snapshot and hash.
- Split it into 1,000-character windows, advancing 800 characters each time
(200-character overlap; offsets use UTF-16 code units). Keep the first window,
then prioritize question-word matches and discussion/conclusion/limitation
passages within
--max-input-chars. - Ask the model to extract assumptions, conclusions, dependencies and open questions, each linked to a supplied passage. A separate model call reviews the extracted claims against those passages.
- Ant colony optimization (
--policy aco) selects the next eligible search/fetch/extract/verify/follow action. Reviewed evidence reinforces the traversed action paths.--policy greedyselects by the fixed heuristic instead; compare policies using separate runs.
Code: passage selection and extraction, scheduler. This runs in one process; the ants are local path simulations. It does not dispatch chunks to room peers. The configured budgets bound this executor's work; they are not a dollar cap.
Inspect a research run — these commands read saved results without model calls:
node tools/research/cli.mjs status --db tools/outputs/credit-assignment.sqlite
node tools/research/cli.mjs tree --db tools/outputs/credit-assignment.sqlite
node tools/research/cli.mjs answer --db tools/outputs/credit-assignment.sqliteCheck status for failures, reviewed sources, reported tokens and tokens reserved
for calls whose usage is unknown. In the browser tree, inspect each claim's exact
passage, dependency links and supported / unsupported / uncertain verdict.
Source offsets and graph structure are checked deterministically; semantic review
is a fallible model judgment, not proof that the research problem is solved.
In rooms, fragment-submit marks a node solved immediately; it does not invoke
this evidence-review pipeline.
Check the software — no model credentials or paid calls needed:
pnpm -C torrent check # lint, typecheck, peer/protocol tests
node --test tests/research-tree.test.mjs # chunking/evidence, budgets, recovery, viewer
pnpm -C site install
pnpm -C site build # typecheck and build the browser game
uv sync # Python tools; requires uv and Python >= 3.13
uv run python -m unittest discover -s tests # corpus/policy testsRoom smoke test: with the two quick-start pears running, use a third terminal:
pnpm -C torrent message -- "hello from the smoke test" --room labBoth feeds should show the message. Quit the coordinator with q: the remaining
pear should become coordinator. Quit it too. For a cross-machine check, repeat
with one pear on each machine on the same tailnet.
Last local audit (2026-09-24): torrent lint/typecheck passed; 60/61 torrent tests
passed (one Discord worker-failure test failed); research-tree tests passed 19/19;
site build passed. Python tests passed 4/7, with three errors from the missing
graphs/corpus_ingest/rwx.md. Two-process room discovery, chat and coordinator
failover passed. Cross-machine operation remains unverified in this checkout.
Someone on the network sends you an invite code; on any laptop:
curl -fsSL https://adiabatic.garden/join | sh -s -- feynman:adiabatic.garden:<inviter>:<key>That installs Tailscale if needed, clones this repo into ~/.feynman/prism,
logs the machine into the feynman tailnet with the invite's single-use key,
asks for a username, records who invited you, and starts the pear. On a
checkout you already have, pnpm -C torrent join -- --invite <code> does the
same from step two. Without an invite, pnpm -C torrent join just sets the
username and launches; pears then only meet over loopback or a tailnet you are
already on.
To invite someone, put HEADSCALE_URL and HEADSCALE_API_KEY in
torrent/.env.local (see hosting below) and run
pnpm -C torrent invite -- --user <their name>; without --user the key is
for another device of your own. If tailscale is
running, pears on every online device of your tailnet see each other too;
--local keeps a pear on loopback.
For several interactive peers on one machine, install tmux (for example,
brew install tmux on macOS or sudo apt install tmux on Ubuntu), then:
pnpm -C torrent orchestrator -- 3 --room lab --join
tmux attach -t pears-lab # reconnect after detaching
pnpm -C torrent orchestrator -- stop --room lab # from another terminalCtrl+B then D detaches and leaves the room running. --join selects a problem
for each pear. On macOS, --terminal opens separate Terminal windows instead.
For model conversations, set OPENROUTER_API_KEY in torrent/.env.local
or use your saved opencode login. Then start DeepSeek pears (requires just
and tmux; assignments make billable model calls):
just pears 3 lab # start or reopen 3 pears in room lab
just pears-attach lab # reconnect
just pears-restart 5 lab # restart with 5 pears; clears conversation histories
just pears-stop lab # stop the roomjust pears defaults to 3 pears in manual-pears. Click a pane, type its problem,
and press Enter; subsequent lines are follow-ups. Ctrl+B then D leaves the room
running. Larger groups use up to four panes per tmux window; click the window
name in the bottom bar to switch. Pears wait for manual assignments and ignore
room chat. PEAR_MODEL optionally selects another deepseek/ model. These
personas have no browsing or experiment tools; use the research executor above
for paper retrieval and evidence review.
Run from the repo root. Room commands take --room <name> to select a room.
| Command | What it does |
|---|---|
pnpm -C torrent pear -- [--room r] [--name n | --index i] [--auto-join id] [--coordinator] [--local] [--home dir] [--bridge 7300] |
one pear (Ink TUI; headless when stdin is not a TTY). --bridge is the loopback port the browser game reads the room from; 0 disables |
pnpm -C torrent orchestrator -- [N=5] [--room r] [--join] [--terminal] |
start N pears in a tmux session (--terminal: macOS Terminal windows); pear #i keeps its identity in torrent/.pears/<room>/<i> |
pnpm -C torrent orchestrator -- stop [--room r] |
kill the tmux session; every pear closes its sockets |
pnpm -C torrent join -- [--invite code] [--home dir] [--room r] [--no-launch] |
one-time setup: tailnet login with the invite, username, then the pear |
pnpm -C torrent invite -- [--user name] [--hours 24] |
mint an invite code (single-use Headscale pre-auth key); needs HEADSCALE_URL/HEADSCALE_API_KEY |
pnpm -C torrent message -- "hi" [--room r] [--as name] [--listen 12] |
one-shot message, listen for replies, exit |
pnpm -C torrent transcript -- [--room r] [--every 30] |
record chat to torrent/snapshots/<room>-<time>.md; stdin lines are sent as [scribe] |
pnpm -C torrent agent -- <persona> [--room r] |
LLM persona pear; persona ids are the keys of the JSON block in PEARS.md. Needs OPENROUTER_API_KEY (or an opencode login); PEAR_MODEL overrides the model |
pnpm -C torrent discord |
Discord program chair with direct persona conversations and bounded delegation; setup below. Still on Hyperswarm, so it does not see pear rooms until it is ported |
pnpm -C torrent discord:costs [YYYY-MM-DD] |
reported OpenRouter usage and costs for a UTC day; defaults to today |
pnpm -C torrent room / node torrent/scripts/guillefix.cjs <room> |
the original Hyperswarm stdin chat client (legacy; not in pear rooms any more) |
pnpm -C torrent fragment-submit -- <problemId> <subproblemId> "<content>" [--spawns "q || q"] [--room r] |
solve a subproblem; --spawns lists the subproblems it uncovered, which the coordinator adds under the solved node |
pnpm -C torrent fragment-velocity -- <fragmentId> <factor> [--room r] |
report downstream speedup; the roads into that node are reinforced (or dropped when the factor stays below 1) |
pnpm -C torrent propose -- <problemId> "<text>" [--parent q3] [--room r] |
propose a subproblem; it waits in the coordinator's review queue |
pnpm -C torrent review -- [--approve all|1,2] [--reject 3] [--room r] |
list the queue, or settle a batch of it |
pnpm -C torrent check |
lint (oxlint + prettier) · typecheck · test |
pnpm -C site dev · pnpm -C site build · pnpm -C site lint |
Changing Shores, the low-poly Three.js game and visuals site (site/README.md) |
uv sync |
Python deps for graphs/ and tools/ |
uv run helm-mirror run |
evaluate feynman research runs, HELM-style (design) |
just ingest |
gate: paper dumps → postgres papers table (needs DATABASE_URL) |
just policy "focused ultrasound" |
rwx-policy: probability distribution over papers + rwx decimals |
just run "focused ultrasound" |
ingest, then policy |
just test · just lint |
Python unittest · ruff |
node tools/research/cli.mjs --help |
one-question SQLite research tree (docs) |
node tools/research/cli.mjs tree --db tools/outputs/credit-assignment.sqlite |
inspect the saved tree generated by the research command above |
Postgres for the gate: export DATABASE_URL="postgresql://postgres@localhost:5432/propagate"
(a local brew postgres with trust auth works).
The application being onboarded is recorded in discord.md. The runtime uses the authenticated bot's Discord ID, so changing its display name does not require changing the router. Its research role is the program chair.
-
Copy
torrent/.env.exampletotorrent/.env.local, which is ignored by git. SetDISCORD_BOT_TOKENfrom the application's Bot page,OPENROUTER_API_KEY,PEAR_MODELto an available OpenRouter model ID, andDISCORD_CHANNEL_IDto a server text channel's ID. Discord Developer Mode exposes Copy Channel ID. An application ID is not a channel ID or token. -
Give the bot View Channel, Send Messages, Read Message History, Create Public Threads, and Send Messages in Threads in that channel. Startup checks the effective permissions, including channel overrides.
-
Run one instance from the repository root:
pnpm -C torrent discord
Select the bot's actual mention in Discord, then type a request:
@your-bot investigate the credit assignment problem
@your-bot aman: propose a formal model
@your-bot gwern: critique Aman's assumptions
@your-bot chair: compare the two approaches
@your-bot status
@your-bot stop
New requests in the configured channel create a thread. Continue in that thread
to share its context; unrelated threads have separate histories. Follow-ups use
the last addressed persona; chair: switches back to orchestration (noera:
is also an alias). representer: and compressor: are available directly.
The bot posts labeled persona replies through a single Discord account.
By default every request needs a bot mention. For plain follow-ups such as
aman: explain that bound in existing bot threads, enable Message Content
Intent in the Discord Developer Portal and set DISCORD_MESSAGE_CONTENT=1.
See Discord Gateway intents.
The bot ignores other channels, bots, and webhook messages. Optional
DISCORD_USER_IDS restricts callers to comma-separated user IDs; otherwise
everyone who can participate in the configured channel can submit requests.
The chair creates a validated plan for Aman and Gwern, runs those two workers
concurrently, optionally calls Representer, and synthesizes one round. Each run
allows at most five model calls, each with a 1,200-token output limit and a
120-second request timeout. Input is bounded to 32,000 characters per call;
the output limit is not a total-token or dollar spending cap. Two conversations
can run at once, with one active run per thread. Busy requests receive a retry
message. status reports progress; the initiating user can stop a run or
resume its last request as a new run. Resuming can incur new charges.
Edit PEARS.md to change personas, then restart. It is the shared
source for Discord and the Hyperswarm agent; the former personas.json has
been migrated there. The Discord workers are model conversations in one local
process. They do not yet browse papers, execute experiments, or dispatch to
remote Hyperswarm peers. Treat generated references as unverified.
Events and OpenRouter-reported usage
are appended under torrent/snapshots/discord/, with UTC daily ledgers and
conversation checkpoints every 30 seconds and at state changes. The logger
does not call a model automatically. Restarted active runs are marked interrupted;
send resume in their thread to start them again with saved context. Costs
missing from provider responses remain unknown. Cancellation stops local work,
but provider charges for interrupted requests may not appear in the ledger.
Use Ctrl-C to stop the process. Local transcript files are ignored by git.
Verification: pnpm -C torrent check. Live smoke: start the bot, mention it with
aman: say hello, check the new thread, then try a chair request, status, and
stop. Run pnpm -C torrent discord:costs to inspect reported usage.
There is no server and no DHT. Every pear listens on the first free port of
7100–7109 and, on a backing-off schedule, dials every port of every host it
can see: loopback always, plus every online device of the tailnet when
tailscale is running (tailscale status --json is the tracker). The first
line on a connection is an id handshake carrying the pear's public key, its
room and its port; a pear in another room is dropped, and when two pears dial
each other both keep the connection the lower key opened. Inbound connections
are only accepted from loopback and Tailscale's own address ranges.
Identity. ~/.feynman/identity.json holds an ed25519 seed and the device
name, both created on the first run (--home or FEYNMAN_HOME picks another
directory). The public key is the pear's id on the wire and will sign
contribution receipts. A corrupt file is refused rather than replaced.
Naming. Every pear names itself: the device name in its identity file,
drawn at random from DESIGN.md's list on the first run. When two pears meet
wearing the same name, the one that joined the room later re-rolls and saves
the new name; --name/--index pin a name that is never yielded. Names are
for humans — the wire identifies pears by public key.
Coordinator. One pear per room coordinates (it will own review routing and assignment). It is the pear that has been in the room longest, ties broken by key; every pear computes the same answer from the hellos it has seen, so there is no election traffic. A pear that starts into an empty room coordinates immediately; if the coordinator quits, the next most senior pear takes over. The header shows who is coordinating.
Messages. Press m, type, enter. Messages go to every connected peer as a
plain line [name] text — the same format the upstream
pear-to-pear tools use, so a pear,
message, transcript and the LLM agents all share one room.
The pear's own control protocol (hellos, joins, renames) travels on the
same sockets as JSON lines prefixed with U+001F; every client skips those, so
humans only see chat.
Orchestrator. Pear #0 gets --coordinator so the room has one from t=0;
each pear keeps its own identity (and so its name) in torrent/.pears/<room>/<i>.
--join gives each pear a different problem. stop kills the tmux session,
which SIGHUPs every pear so it closes its sockets cleanly. Without tmux (or
with --terminal) it opens one macOS Terminal window per pear instead; quit
those with q.
Problem graph. The coordinator keeps every problem's subproblems, the
dependencies between them and the proposal queue in <home>/graph.sqlite
(node:sqlite, no native module) and broadcasts a snapshot of the problem
after each change; other pears render that snapshot, and a pear that takes
over as coordinator absorbs the last snapshot it saw before writing. The graph
is seeded once from torrent/src/data.ts (q1…qN per problem) and then grows
on its own. A submitted fragment marks its node solved, adds each --spawns
subproblem as a child that required it, and logs which open nodes it
unlocked. A velocity report multiplies the weight of every road into that
node by the factor; a road that decays below 0.05 is dropped, since a
dependency that never sped anything up was not one. Those two changes need no
one's approval. Human proposals (p in the TUI, pnpm propose) queue up
instead and are settled in batches (r as coordinator, pnpm review).
Expanding a problem shows the tree: ✓ solved, ○ ready, · blocked on an
open requirement.
Islands. The pear's loopback bridge (--bridge, port 7300) also serves
the research graph to the browser game in site/: GET /world?problem=,
GET /research (an event stream with every problem's world on connect and
after each change), POST /explore and POST /walk. torrent/src/world.ts
turns a snapshot into islands (nodes, shape hashed from the id, position from
a deterministic force layout), causeways (requires-edges), roads, trails and
shoals (pending proposals); the game scales it onto the sea southwest of
Arcadia. Walking from one island to another is a walk; three walks between
the same two wear a road in, which the layout then pulls closer. Explore
(sail off the map to search OpenAlex for new land) is served but the game
does not call it yet.
Personas are the JSON block in PEARS.md (read at startup by the
LLM agents). The stirrer walks the problem list in torrent/src/data.ts, the
single source of truth for names and problems.
The tailnet's control plane is Headscale
behind Caddy, defined in infra/headscale/ (Caddy also serves /join).
On a small VPS with ports 80 and 443 open and DNS for adiabatic.garden
pointing at it:
scp -r infra/headscale you@vps:feynman && ssh you@vps
cd feynman && docker compose up -d
docker compose exec headscale headscale users create <you>
docker compose exec headscale headscale apikeys create --expiration 90d # → HEADSCALE_API_KEYBack on your machine, set HEADSCALE_URL=https://adiabatic.garden and the API
key in torrent/.env.local, then pnpm -C torrent invite -- --user <you> and
run the printed curl … | sh -s -- <code> line on each of your devices. Every
device on the tailnet is visible to every pear; there is no other server. To
use another domain, change it in Caddyfile, config.yaml (server_url,
dns.base_domain) and site/join.sh.
connectingfor a long time. Loopback pears connect within a second; tailnet pears within one sweep (3 s, then backing off to 60 s). Checktailscale statusshows the other device online, and that both pears use the same--room.- Two pears on one machine share a name or fight. They share
~/.feynman/identity.json; give the second one--home <dir>. EADDRINUSE. All ten ports7100–7109are taken:pgrep -fl pear.tsxfinds leftover pears. Pears close their sockets onq, Ctrl-C, SIGTERM and SIGHUP, so this should only happen after a hard kill.- Two coordinators. Two pears that both started into an apparently empty room self-elect; on meeting, the earlier start wins, the other gives up its coordinator role. Device names are resolved separately if they collide.
README.md AGENT.md DESIGN.md CONTEXT.md CHANGELOG.md PEARS.md
torrent/ the pear: p2p client over loopback / tailscale (TypeScript, Ink)
src/ wire · identity · invite · headscale · transport · room · state · send · presence · naming · peer · lifecycle · ui/ · pear.tsx (entry) · discord/ (wip, hyperswarm)
scripts/ join · invite · orchestrator · message · transcript · agent · guillefix.cjs
infra/headscale/ adiabatic.garden: Headscale + Caddy compose, config, and the /join script
site/ Changing Shores: Vite + React shell, src/game/ Three.js engine (visual-design.md)
tests/ node:test over the pure modules (wire, room, naming)
autoresearch/ vendored feynman autoresearch
tools/ helm_mirror (evaluator) · research (SQLite research tree) · instructions · outputs/ (untracked)
graphs/ corpus/ (paper dumps, open problems) · corpus_ingest/ (gate, rwx policy)
tests/ Python + node tests for graphs and tools
docs/ in-short.md (the proposal), images
Style and limits for contributors and agents: AGENT.md. Design notes: DESIGN.md. History and scrap notes: CONTEXT.md. Acoustics (CT viewer, jwave simulation) moved to exanova-y/propagate-yourself.
