Description
๐๏ธ Flair



> The identity and memory substrate for AI agents. Crypto-pinned. Federated. Self-hosted.
Every agent framework gives you chat history. None give you identity. Flair gives an agent three things that survive a restart and follow it between orchestrators: an identity it proves with an Ed25519 keypair, memory it searches by meaning rather than by keyword, and a soul โ the personality, values and procedures that make it that agent.
Quick start
Runs on a laptop, a VPS, or anywhere Node does. Needs Node.js 22+.
1. Install the CLI (no sudo)
npm install -g @tpsdev-ai/flair2. Verify the command is on your PATH
flair --version3. Bootstrap the instance and register an agent
flair init --agent mybot4. Write a memory
flair memory add --agent mybot "Harper v5 sandbox blocks node:module but process.dlopen works"5. Find it back by meaning, not by keyword
flair search --agent mybot "native addon loading in sandboxed runtimes"
If flair --version prints command not found, your npm global bin directory is not on PATH:
export PATH="$(npm prefix -g)/bin:$PATH"
Re-run flair --version to confirm, then continue.
Step 5 finds the memory you never keyword-matched:
Harper v5 sandbox blocks node:module but process.dlopen works
( 2026-07-28 ยท standard ยท 100% )
By default, that percentage displays semantic similarity with a small keyword-match
boost. Hybrid search combines semantic and lexical ranks to order results, so the
first result need not have the highest percentage or be near 100%. The percentage
is not a probability that the memory answers your question correctly.
flair init --agent installs or reuses Harper, creates or reuses the agent’s Ed25519 keypair, and checks semantic search. When wiring is enabled, it attempts to wire detected clients and may run an MCP smoke test for MCP client wiring.
> Pass --agent. A bare flair init bootstraps the instance and stops there โ no agent, no keypair, no MCP wiring.
Full walkthrough with expected output at every step: docs/quickstart.md.
One install, one binary
npm install -g @tpsdev-ai/flair puts a single command on your PATH: flair. That is the whole install, and it needs a user-writable npm global prefix โ which is why step 1 says no sudo. A root-owned install can’t write the embedding model into its own package directory, so semantic search silently degrades to keyword-only. Use nvm, or point npm at your home directory โ npm config set prefix ~/.npm-global, then add ~/.npm-global/bin to PATH. flair init and flair doctor both check for this and say so loudly.
> Known install-size issue. npm install -g @tpsdev-ai/flair currently pulls roughly 130 MB of React Native tooling that a Node install never uses โ an optional dependency of Harper’s SQL engine. It is inert and Flair runs normally. The durable fix is upstream (harperfast/harper#1937). Do not install with --omit=optional to avoid it: that also drops Harper’s platform RocksDB binding, and Flair will not start.
Two MCP paths, on purpose. A client on this machine and a remote connector are different trust situations, and each has its own mechanism. The write-up that joins them is docs/mcp-clients.md โ Two MCP paths.
- Local client: the
npxstdio adapter.@tpsdev-ai/flair-mcpis a separate package and is not installed globally.flair init --agentattempts to wire detected clients when wiring is enabled, usingnpx -y @tpsdev-ai/flair-mcp@pinned to the CLI’s version. The adapter sends requests toFLAIRURL, signing with an Ed25519 key when one resolves and otherwise using configured administrator Basic credentials when available. Loopback HTTP is the right scheme on this machine (the default ishttp://127.0.0.1:19926). A remoteFLAIRURLshould use HTTPS. This is the path for Claude Code, Cursor, Codex, Gemini, and every other local MCP client. - Remote client: native
/mcp. A JSON-RPC endpoint inside the server, OAuth bearer, exposing the curated tools that the JSON-RPCtools/listmethod on/mcpreturns. It is off by default: an unsetFLAIRMCPOAUTHleaves/mcpunregistered; a truthy flag and a configured issuer make Flair attempt to mount it, while only the literal valuetruealso enables the OAuth component.trueis the value that turns on both Flair’s route and the OAuth component;1,yes, andonleave the component off and every call 401s. OAuth client-metadata is fetched over HTTPS and refuses a private, loopback, or link-local host. Remote connectors need a publicly reachable HTTPS origin;flair mcp enableaccepts loopback or unspecified targets for the local restart but refuses local issuers, while route registration itself does not enforce a public HTTPS issuer.
@tpsdev-ai/flair-client is likewise its own package: add it to a project when you want to call Flair from your own code (JavaScript / TypeScript).
Where the agent’s key lives
flair init --agent mybot writes the private key to ~/.flair/keys/mybot.key (mode 0600) and the public half beside it as mybot.pub. Only the public key is registered on the instance; the private key never leaves the machine. --keys-dir writes both somewhere else.
Back that file up โ it is the agent’s identity, and there is one copy. Memories are not encrypted with it, so losing it costs the identity, not the data: the agent can no longer sign, and every HTTP call it makes fails. Recovery is flair agent rotate-key mybot, which mints a new pair and re-registers the public half โ it needs the admin password flair init wrote to ~/.flair/admin-pass, so back that up too. Otherwise treat the key like an SSH key: one per agent per host, never copied between machines (docs/secrets-and-keys.md).
Keys are how an agent outside the process proves who it is. Code running inside the same Harper instance needs no key at all โ see Embedded in a Harper app.
Useful flags
flair init --agent mybot --client claude-code # attempt to wire one client
flair init --agent mybot --no-mcp # register the agent; skip MCP wiring
flair init --agent mybot --skip-soul # skip the personality wizard
flair init --agent mybot --port 8000 # use a non-default port
Lifecycle
flair status # instance health, memory stats, agents
flair doctor # diagnose common problems and suggest fixes
flair stop # stop the instance
flair restart # restart it
flair uninstall # remove the service, keep data and keys
flair uninstall --purge # remove data, keys, secrets, schedulers, and client wiring
# (the npm package is left; npm uninstall -g @tpsdev-ai/flair)
Upgrading
flair status and flair doctor compare your installed version against the latest published release and print a nudge when you’re behind:
โ flair 0.16.1 is behind โ latest is 0.20.1 (4 releases behind). Upgrade: npm i -g @tpsdev-ai/flair@latest
flair upgrade --check # show the plan across flair, flair-mcp and the openclaw-flair plugin
flair upgrade # apply it; the instance restarts automatically
A plain extracted tree (npm pack + systemd, not npm install -g)? flair upgrade detects it, or pass --tree . See docs/upgrade.md.
Deployed to a Harper Fabric cluster instead of running locally? Use flair upgrade --target .
What it looks like
!Flair: write a memory, find it by meaning, list of supported harnesses
Write a memory, then find it by meaning. The same memory is visible to every harness in the catalog.
!Flair cross-orchestrator: one agent, one memory store, three MCP-capable CLIs
One Ed25519 identity, one memory store, three MCP-capable CLIs. A memory written from Claude Code is retrievable from Codex CLI and Gemini CLI a moment later. Identity and history aren’t bound to one orchestrator’s runtime.
One agent, every harness
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ same agent, same memory, every harness โ
โ โ
โ Claude Code โโ โ
โ Cursor โโค โ
โ Codex CLI โโผโ[ flair-mcp ]โโ โ
โ Gemini CLI โโค โ โ
โ Continue.dev โโค โ โโโโโโโโโโโโโโโโโโโโโโโโ โ
โ Goose โโ โโโถ โ Flair (self-hosted) โ โ
โ LangGraph โ[ langgraph-flair ]โโโ Ed25519 / HNSW / โ โ
โ OpenClaw โ[ openclaw-flair ]โโโ Soul + Memory โ โ
โ n8n โ[ n8n-nodes-flair ]โโโโโโโโโโโโโโฌโโโโโโโโโโโโ โ
โ Hermes โ[ hermes-flair ]โโ โ federation โ
โ Pi agent โ[ pi-flair ]โโ โ (hub/spoke) โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Flair (Fabric hub) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Choose your harness from the full integrations catalog โ.
How it works
Flair is a native Harper v5 application. Harper handles HTTP, persistence (RocksDB), and application logic in one process โ self-hosted, with no sidecars, no vector database and no embedding API.
Agent โโ[Ed25519-signed request]โโโถ Flair (Harper)
โโโ Auth middleware (verify signature)
โโโ Identity (Agent + Integration tables)
โโโ Memory (write โ auto-embed โ store)
โโโ Soul (permanent personality/values)
โโโ Search (semantic + keyword, ranked)
Embeddings are generated in-process by nomic-embed-text (768-dim) via a Harper plugin, on CPU or GPU (Metal, CUDA). No API calls, no sidecar, no network hop. Supply-chain policy covers the bake-time and dependency pinning that keep it that way.
Requests are signed as agentId:timestamp:nonce:METHOD:/path and verified against the agent’s registered public key, with a 30-second replay window and nonce deduplication.
See DESIGN.md for the invariants behind the three primitives โ why they’re shaped the way they are.
Features
| Feature | What it does |
|—|—|
| Semantic memory | Auto-embedded on write. Search by meaning, not keywords. |
| Tiered durability | Four tiers; each states what it does and does not guarantee. permanent โ routine maintenance never reaps or age-archives it (an expired validTo archives an eligible row; an acquired expiresAt never reaps it); it never decays; bootstrap considers the bootstrapping agent’s own permanent memories before recent rows, subject to scope, expiry/closure and the token budget. persistent โ routine maintenance never reaps or age-archives it (an expired validTo archives an eligible row; an acquired expiresAt never reaps it). standard โ routine maintenance archives it once its validTo passes or, as a session note, after 30 days. ephemeral โ routine maintenance reaps it once its TTL (24h by default) passes. No tier adds a flush, fsync, backup or replica acknowledgement: an explicit delete (owner or admin) or a store failure can end any of them. |
| Temporal validity | validFrom / validTo bounds. Expired memories drop out of search and bootstrap automatically. |
| Trust-graded recall | Opt-in per-result evidence: provenance, usage signal, freshness, supersession. Confidence bands (strong/moderate/breadcrumb) and first-class abstention when nothing clears the floor. |
| Relationship graph | Entity-to-entity triples with temporal bounds, queryable alongside semantic memory. |
| Auto entity detection | Entities extracted from memory content on write. No tagging required. |
| Predictive bootstrap | Cold-start context selected from active project, recent activity and agent role โ not just recency. |
| Multi-agent | One instance, any number of agents, each with its own keys, memories and soul. |
| Federation | Hub-and-spoke sync between instances using signed requests and pairing tokens. Non-hub peers sync only rows treated as self-originated; the nonce store rejects replayed signed requests. docs/federation.md |
| Memory hygiene (REM) | On-demand (flair rem rapid) and scheduled nightly distillation. flair rem promote requires a rationale; nightly REM auto-promotes eligible ADK and continuity candidates. docs/rem.md |
| Memory bridges | Import/export to foreign memory systems via a YAML descriptor or a code plugin. docs/bridges.md |
| Real-time feeds | Administrators subscribe to table changes over WebSocket/SSE; verified agents subscribe to changes to the memories they can read through /FeedMemories, and to soul changes through /FeedSouls. |
| OAuth 2.1 server | PKCE, dynamic client registration, standards-compliant token endpoint. Delegate auth to Flair without a separate IdP. docs/auth.md |
| XAA | Bind agent identities to Google Workspace, Azure AD or Okta accounts. |
| Web admin | Server-rendered UI for principals, connectors, IdPs and instance config. No separate dashboard service. |
| Benchmarks | flair-bench scores candidate embedding models against the same recall corpus Flair’s CI gates on. Runs standalone โ no Flair install, no server. |
Trust-graded recall reaches the authenticated HTTP API and the built-in /mcp tools today โ the latter being off by default, see One install, one binary. CLI, @tpsdev-ai/flair-client and flair-mcp exposure is a follow-up. REM needs a configured generative backend (Ollama, OpenAI, Anthropic, โฆ) โ without one, flair rem rapid fails with Reflection error: No generative backend configured.
How Flair compares
Every product here does semantic recall over stored memories. These are the dimensions they actually differ on.
| | Flair | Mem0 | Honcho | Letta (MemGPT) | SageOx | Built-ins (OAI/Anthropic/Google) |
|—|—|—|—|—|—|—|
| Where memories live | infrastructure you run | self-host or Mem0 Cloud | self-host or hosted API | self-host or Letta Cloud | SageOx cloud | vendor cloud |
| Memory is scoped to | the agent, via an Ed25519 keypair | tenant / user | per-user tenant | the runtime | the team | the account |
| Reaches other orchestrators | Coding assistants, workflow engines, and agent frameworks (catalog) | several | several | Letta’s runtime | 14+ coding agents and editors, via hooks, plugins and instruction files | no |
| Sync between instances you run | hub/spoke federation | no | no | no | one hosted service | one hosted service |
| Captures in-person conversation | no | no | no | no | yes โ Ox Dot | no |
| Per-agent persistent character | first-class (Soul) | optional | persona-shaped | optional | team context, not per-agent | no |
Where Flair loses. SageOx’s Ox Dot records in-person meetings, standups and whiteboard sessions and pipes them into shared context; Flair has no ambient capture of anything that isn’t already text in a tool. Mem0’s hosted sync is more polished. Honcho’s persona model is more developed. Letta’s integration with its own agent loop is tighter than anything Flair offers. And there is no Flair-operated cloud โ running it is your job.
Integration
Flair works with any agent runtime. Pick the path that fits yours โ full catalog.
Claude Code / Cursor / Codex CLI / Gemini CLI (MCP)
flair init --agent attempts to wire detected clients when wiring is enabled. To configure one by hand:
// .mcp.json in your project root (Claude Code / Cursor format)
{
"mcpServers": {
"flair": {
"command": "npx",
"args": ["-y", "@tpsdev-ai/flair-mcp"],
"env": { "FLAIRAGENTID": "mybot" }
}
}
}
Then add to your CLAUDE.md:
At the start of every session, run mcpflairbootstrap before responding.
(mcpflairbootstrap is Claude Code’s namespaced name for the server’s bootstrap tool.) Use flair bootstrap --agent when MCP is not wired โ previewing context, scripts, or any agent that can run a shell command.
The flair-mcp server exposes memorystore, memorysearch, memoryupdate, memoryget, memorydelete, relationshipstore, bootstrap, soulset, soulget, flairworkspaceset and flair_orgevent. Memory follows the agent across CLIs โ same instance, same identity, switch harness without losing state.
Per-CLI config snippets (Gemini CLI’s ~/.gemini/settings.json, Codex CLI’s ~/.codex/config.toml) are in docs/mcp-clients.md; a deeper Claude Code walk-through is in docs/claude-code.md.
OpenClaw
openclaw plugins install @tpsdev-ai/openclaw-flair
Auto-detects the agent identity, provides memorystore / memorysearch / memoryget, and provides bootstrap context from beforeprompt_build (requires host opt-in). See the plugin README.
n8n
Settings โ Community Nodes โ Install โ @tpsdev-ai/n8n-nodes-flair
Three nodes: Flair Chat Memory (Memory port), Flair Search (Tool port), Flair Write (Main connections). Setup and security guidance in docs/n8n.md โ read the auth note below first.
Flair CLI
Any agent that can run a shell command can use Flair directly.
flair memory add --agent mybot "learned something important"
flair search --agent mybot "that important thing"
flair soul set --agent mybot --key role --value "Security reviewer" --admin-pass-file ~/.flair/admin-pass
flair bootstrap --agent mybot --max-tokens 4000 # cold-start: soul + relevant memories
flair backup --admin-pass-file ~/.flair/admin-pass # logical JSON export
FLAIRADMINPASS="$(cat ~/.flair/admin-pass)" flair restore ./backup.json
For commands that declare --admin-pass-file, prefer it over --admin-pass to keep the secret out of ps and your shell history.
JavaScript / TypeScript
npm install @tpsdev-ai/flair-client
import { FlairClient } from '@tpsdev-ai/flair-client'const flair = new FlairClient({
url: 'http://localhost:19926', // or remote: https://flair.example.com
agentId: 'mybot',
// key auto-resolved from ~/.flair/keys/mybot.key
})
await flair.memory.write('Harper v5 sandbox blocks bare imports')
const results = await flair.memory.search('native module loading')
const ctx = await flair.bootstrap({ maxTokens: 4000 })
await flair.soul.get('role')
No Harper, no embeddings โ just HTTP and auth. Full API in the client README.
HTTP API (any language)
Anything that can make an HTTP request and sign with Ed25519.
curl -H "Authorization: TPS-Ed25519 mybot:$TS:$NONCE:$SIG"
-X POST http://localhost:19926/SemanticSearch
-d '{"agentId": "mybot", "q": "deployment procedure", "limit": 5}'curl -H "Authorization: TPS-Ed25519 mybot:$TS:$NONCE:$SIG"
-X PUT http://localhost:19926/Memory/mybot-123
-d '{"id": "mybot-123", "agentId": "mybot", "content": "...", "durability": "standard"}'
Sign agentId:timestamp:nonce:METHOD:/path with the agent’s private key. Protocol in SECURITY.md. Consolidated endpoint, auth-per-resource, and schema catalog: docs/api-reference.md.
Embedded in a Harper app (in-process)
The second front door, and the one to take if your code already runs on Harper.
Flair is a Harper component. Deploy it into the instance your application already runs in and call its resources directly โ await h.post({ agentId, content }) is a method call, not a network call. No second service to operate, no HTTP round trip, and no key to distribute: a caller in the same process is already inside the trust boundary and names the agent it is acting as, per call.
Adding it takes nothing away. The HTTP surface keeps serving MCP clients and remote agents exactly as before.
import { server } from "harper";
import { agentContext, collectionResource } from "@tpsdev-ai/flair/dist/resources/in-process.js";// The RESOURCE โ auth, read-scoping, visibility, embedding.
// Registry keys carry no leading slash: get("Memory"), never get("/Memory").
const Memory = server.resources.get("Memory").Resource;
const h = await collectionResource(Memory, agentContext("mybot"));
await h.post({ agentId: "mybot", content: "...", durability: "standard" });
databases.flair.Memory is the table (raw storage); the exported Memory class is the resource, where auth, read-scoping, visibility and embedding live. new Memory(...) is not a substitute for collectionResource โ a create needs a collection-bound instance only Harper can produce. Both helpers refuse a missing agent id rather than defaulting it, because a resource invoked with no context resolves to Flair’s trusted internal verdict and runs unfiltered across every agent.
This path needs no key at all. Keys are how an agent outside the process proves who it is. Code running inside the same Harper instance asserts identity through the call context instead โ agentContext("mybot") โ which Flair reads and acts on with no signature, no Agent-table lookup and no registration. That is deliberate: same-process code could write the storage tables directly, so demanding a signature from it would be theatre. It is also why that id must come from your own server-side state and never from request data.
โ Embedding Flair in a Harper app โ the whole in-process contract, measured against a real instance: resolving the resource, the table-vs-resource distinction that decides whether your memories are scoped at all, N agents in one process, and registering agents with no shell on the node.
Auth across surfaces
Ordinary agents using Ed25519 keys sign network requests. They can write only their own records and read their own plus other agents’ non-private memories. Administrator agent roles and administrator Basic credentials have broader authority, including access to private memories. Stdio MCP, Pi, LangGraph, and the wake runner can use administrator Basic auth when no signing key resolves; n8n requires a Harper administrator password in its credential; its node requests use FlairClient, which prefers a resolving Ed25519 key and otherwise uses the configured Basic credentials. See SECURITY.md.
Treat n8n’s credential as an instance-wide administrator credential: a workflow can read private memories and write under any agent ID. Use it only on an operator-controlled, single-tenant n8n instance with trusted workflow inputs. Full breakdown: docs/auth.md.
In-process callers are a different model, not an exception to this one: they never sign, because identity is asserted through the call context rather than proven. Co-location is the grant โ which is why Flair beside untrusted co-tenants on a shared instance is a different proposition to Flair inside your own app.
Deployment
Local (default)
flair init --agent mybot
Data stays on your machine. A single Harper process โ no Docker, no cloud, no external services. If port 19926 is taken, flair init --port 8000 records the choice in ~/.flair/config.yaml for future commands.
Remote Server
Run Flair on a VPS or cloud instance; agents connect over HTTPS.
On the server
flair init --agent mybot --port 19926On each client โ --target seeds the agent on the remote instance
flair agent add otherbot --target https://your-server:19926
--target derives the operations API as REST port โ 1. If that isn’t where your ops API lives, pass --ops-target explicitly. Good for teams with multiple machines or always-on agents.
Harper Fabric
Managed hosting with multi-region replication and failover. Need a public URL for Cursor / Grok Bot / cloud agents? Start at docs/quickstart-fabric.md. Federation, pairing, and operator detail: docs/deploying-on-fabric.md.
Security
Full model, threat analysis and recommendations in SECURITY.md.
Memory.post() and Memory.put() are the calls behind flair memory add, MCP memorystore and the SDK writes. A new memory they write without a visibility takes a default from durability: permanent/persistent default to shared, standard/ephemeral to private. A write that names neither lands private, and an update through Memory.put() that names no visibility keeps the stored one. Their write response names the visibility the record landed on whenever the record has one; the admin-only reindex re-PUT, which rebuilds indexes for an existing row, does not return that response. POST /FeedMemories does not apply this default: a new ephemeral feed record without visibility lands private, any other new feed record without visibility has no visibility field and reads as non-private, an update that names no visibility keeps a stored private/shared value, and the response (the stored record) names visibility only when the record has one. Say what you mean with --visibility shared|private (CLI) or visibility (MCP / SDK / feed).flair init and written to ~/.flair/admin-pass (mode 0600). The CLI prints the path, never the value. Prefer --admin-pass-file over --admin-pass so it stays out of ps and shell history.flair agent rotate-key./mcp (which people, which apps, what they can reach) and how to revoke access: docs/access-control.md.Architecture
flair/
โโโ src/cli.ts # CLI: init, agent, status, backup, grant
โโโ config.yaml # Harper app configuration
โโโ schemas/*.graphql # Agent, Memory, Soul, Federation, OAuth tables
โโโ resources/
โ โโโ auth-middleware.ts # Ed25519 verification + agent scoping
โ โโโ embeddings-provider.ts # In-process nomic embeddings
โ โโโ Memory.ts # Durability enforcement + auto-embed
โ โโโ Soul.ts # Permanent-by-default personality
โ โโโ SemanticSearch.ts # Hybrid semantic + keyword search
โ โโโ MemoryBootstrap.ts # Cold-start context assembly
โ โโโ MemoryFeed.ts # Real-time memory changes
โโโ packages/ # flair-mcp, flair-client, openclaw-flair, n8n-nodes-flair,
โ # langgraph-flair, hermes-flair, pi-flair, flair-bench
โโโ SECURITY.md # Threat model + auth documentation
Key decisions:
@table @export auto-generates REST CRUD; custom resources add durability guards, auto-embedding and search.Development
bun install # install dependencies
bun run build # compile TypeScript โ dist/
bun test # unit + integration tests
Integration tests spin up a real Harper instance on a random port and tear it down afterwards โ no mocks for the database layer. Every commit runs unit tests, integration tests, type check, dependency audit, Semgrep and CodeQL SAST, and a from-scratch Docker validation. See CONTRIBUTING.md.
Status
Flair is in active development and daily use. We dogfood it: the agents that build Flair use Flair for their own memory and identity.
Shipped: Ed25519 identity and auth ยท memory CRUD with durability enforcement and near-duplicate detection ยท in-process semantic embeddings ยท hybrid search with temporal intent detection ยท soul ยท relationship graph ยท auto entity detection ยท temporal validity ยท real-time feeds ยท predictive bootstrap ยท federation ยท OAuth 2.1 and XAA ยท web admin ยท REM memory hygiene ยท MCP server ยท OpenClaw plugin ยท client library ยท portable agent export/import ยท flair upgrade.
Next: git-backed memory sync ยท opt-in encryption at rest (AES-256-GCM per memory).
> Note: Flair runs on Harper v5. We run it in production daily and track upstream closely. Pin your Harper version.