Chats are disposable; Ferumind is where the continuity lives.
Ferumind is a Markdown workspace shared by you and your AI agents. It runs on your own machine, the documents stay readable and editable on disk, and a stateless MCP server handles project scoping, retrieval, guarded edits, snapshots, and an auditable history of every change.
It is built to be used from a web chat client. An outbound relay dials from your machine to ChatGPT, so the workspace is reachable from the chat you are already in, with no inbound port and no public hostname. Local clients like Claude Code and Cursor connect over stdio.
Beta, distributed as a source checkout, on Python 3.12-3.14. The product
contract is in product/; where code and
product/spec-mcp.md disagree, the spec wins.
Linux, Python 3.12-3.14, uv, and
just. AGENTS.md lists the
plain uv equivalent of every just recipe.
git clone https://github.com/MetaphoricalSheep/ferumind.git
cd ferumind
just setup && just bootstrap && just verifyThe workspace defaults to ./workspace inside the checkout. It is your data,
so keep it somewhere else:
cp .env.example .env
echo 'FERUMIND_WORKSPACE=/home/you/ferumind-workspace' >> .env
just bootstrapYou can skip this. Once connected, ask your agent to make one and it will call
create_project; the CLI and the tool run the same code. To do it yourself:
uv run ferumind project create notes --title "My Notes"Either way you get a registry entry plus a folder skeleton with a seeded spine and rules.
The main way to use Ferumind. tunnel-client dials out from your machine over
the OpenAI Secure MCP Tunnel,
so nothing listens for inbound connections. Full MCP support in ChatGPT is
currently a developer-mode feature.
Put CONTROL_PLANE_TUNNEL_ID and CONTROL_PLANE_API_KEY in .env, then:
just tunnel --doctor # validate, start nothing
just tunnel --init # create the profile if absent, then start
just tunnel-bg # run detacheddocs/tunnel.md covers the variables, where secrets may live, and stopping a background tunnel. Read the access model first: the relay credentials are the only thing guarding your workspace.
Claude web is not supported. It needs a public URL, which the OpenAI tunnel does not provide, and standing one up is exactly what the gate below governs.
One paste, once per project.
product/contract/bootstrap.md holds the
prompt, and just bootstrap installs a copy to
workspace/system/prompts/bootstrap.md.
- Create a project in ChatGPT.
- Copy the text below the
---into its instructions. - Replace
<PROJECT_KEY>with your project key, for examplenotes.
Every new chat in that project then calls get_context before answering, and
reads the workspace rules, the spine, the document map, and the skills index
instead of starting from nothing.
Claude Code, Claude Desktop, and Cursor run the server over stdio. Configure it in the client, not in this repository:
{
"mcpServers": {
"ferumind": {
"command": "/absolute/path/to/ferumind/scripts/ferumind-mcp-stdio"
}
}
}The path must be absolute, because clients spawn the server from their own
working directory. Point at the wrapper rather than uv run ferumind mcp serve: it loads .env and strips control-plane credentials before starting.
Keep FERUMIND_WORKSPACE in .env so one setting covers every client. The
server appears as Ferumind with 48 tools.
Operator diagnostics stay on the machine that owns the workspace:
uv run ferumind doctor
uv run ferumind lint
uv run ferumind observations errors --since 24h
uv run ferumind dashboard --openThe dashboard listens only on 127.0.0.1:8765. It is a separate, read-only
operator process rather than an MCP transport, and it continues to render a
degraded diagnostic view when the MCP server is stopped. See
docs/observability.md for correlation-ID lookup,
privacy guarantees, SSH forwarding, and Basecoat theme maintenance.
docs/lint.md documents the separate, report-only workspace
lint command and what each check means.
Ferumind serves one person: the owner, running it themselves. Direct SSE and streamable HTTP transports are disabled in code.
The optional operator dashboard is a distinct loopback-only HTTP surface. It has no mutation endpoints, is never attached to the MCP tunnel, rejects non-local Host headers, and loads no external assets. It does not make remote MCP serving supported.
- The MCP server does not authenticate callers. The relay and its
CONTROL_PLANE_*credentials are the only access control in front of your workspace. Treat them as granting full read and write access to everything in it. - The relay provider sits in the data path. Your document and memory payloads transit it.
- Account scoping and
tunnel_idare convenience, not server-verified identity or workspace authorization.
Serving anyone other than yourself needs OAuth, deny-by-default subject-to-workspace authorization, and the deployment review in SECURITY.md, which also holds the threat model and the reporting process.
The public-tree release check is a necessary current-tree control, not approval to publish by itself: it classifies tracked paths but does not inspect file contents or Git history. Review both for secrets before making a repository public. Live workspace content, databases, environment files, and generated agent configurations are Git-ignored.
Ferumind is 0.MINOR.PATCH. Pin a tag — tags mark smoke-tested commits and
never move once pushed:
git checkout v0.1.0main moves. It is always green, but it is the integration line, not a
release.
The version number promises one thing: a breaking change bumps the minor.
0.3.1 → 0.3.4 will not break you; 0.4.0 might, so read the
changelog first. Nothing enforces that for you in Python —
write ferumind>=0.3,<0.4 yourself.
It does not promise backports, a deprecation window, a supported release channel, or a wheel, PyPI package, or container image. The Python import API is private and unversioned; use the MCP server or the CLI. docs/releases.md has the detail, including exactly which surfaces are covered.
Interface MCP server · CLI · local operator dashboard
Core domain paths · registry · documents · policy · search
patches · writes · snapshots · operations · diagnostics
System state Markdown workspace · SQLite index/history · private runtime events
Documents get their role from their folder. Editing is lookup-first and guarded
by propose-then-apply, every mutation is snapshotted and logged, and search
runs on SQLite FTS5. Call observations and exceptional runtime events record
metadata only, never content. The CLI and local dashboard use one typed core
diagnostic/query layer rather than embedding diagnostic SQL in either UI.
Core safety logic lives in src/ferumind/core; the MCP and CLI layers call it
rather than duplicating it.
Edits made outside Ferumind are caught by reconcile-on-read. The next read that touches a path stats it against the index, reindexes on drift, marks pending proposals stale, and writes a log entry. There is no filesystem watcher.
The server is a librarian rather than an autonomous agent. The documents carry the behavior contract; the connected chat agent exercises judgment.
workspace/
system/ rules, skills, bootstrap prompt, templates, registry,
format marker
projects/<key>/ spine.md, rules, canvases, memory, library, inbox, archive
compacts/
.ferumind/ private database, snapshots, blobs, backups
Run it as the OS account that owns the workspace, and never commit
workspace/.
.ferumind/ grows: every edit keeps a snapshot of what it replaced, every
applied patch keeps its diff, and each ferumind migrate leaves a full backup
tarball. That is deliberate — snapshots are the only copies of superseded
versions — but it does not shrink on its own.
ferumind prune # what could be reclaimed; deletes nothing
ferumind prune --apply # actually reclaim itPrune only ever touches Ferumind's own bookkeeping under .ferumind/. Your
documents are not part of it, archive/ included — archiving is how you retire
a document, not how you delete one. Stop the tunnel before --apply; the run
rewrites the database and needs it to itself.
just verify # format, lint, strict Pyright, tests, coverage floorSee CONTRIBUTING.md, AGENTS.md,
docs/releases.md,
docs/dependencies.md,
docs/python-support.md, and
docs/mcp-sdk-support.md.