@tpsdev-ai/flair 0.25.4 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +2 -0
  2. package/dist/cli.js +998 -50
  3. package/dist/doctor-client.js +47 -0
  4. package/dist/rem/runner.js +60 -3
  5. package/dist/resources/MemoryDedupStats.js +161 -0
  6. package/dist/resources/dedup-cluster.js +145 -0
  7. package/dist/resources/health.js +33 -1
  8. package/dist/resources/mcp-oauth.js +4 -2
  9. package/docs/assets/flair-cross-orchestrator.cast +33 -0
  10. package/docs/assets/flair-cross-orchestrator.gif +0 -0
  11. package/docs/assets/flair-demo.cast +18 -0
  12. package/docs/assets/flair-demo.gif +0 -0
  13. package/docs/auth.md +178 -0
  14. package/docs/bridges.md +291 -0
  15. package/docs/claude-code.md +202 -0
  16. package/docs/deployment.md +204 -0
  17. package/docs/entity-vocabulary.md +109 -0
  18. package/docs/federation.md +179 -0
  19. package/docs/integrations.md +197 -0
  20. package/docs/mcp-clients.md +240 -0
  21. package/docs/n8n-management.md +142 -0
  22. package/docs/n8n.md +122 -0
  23. package/docs/notes/adk-spike-findings-2026-05-05.md +331 -0
  24. package/docs/notes/mcp-agent-auth-consumer.md +229 -0
  25. package/docs/notes/mcp-oauth-model2.md +132 -0
  26. package/docs/notes/rem-ux.md +39 -0
  27. package/docs/openclaw.md +131 -0
  28. package/docs/quickstart.md +153 -0
  29. package/docs/releasing.md +173 -0
  30. package/docs/rem.md +60 -0
  31. package/docs/rerank-provisioning.md +67 -0
  32. package/docs/secrets-and-keys.md +173 -0
  33. package/docs/spoke-bringup.md +303 -0
  34. package/docs/supply-chain-policy.md +156 -0
  35. package/docs/system-requirements.md +42 -0
  36. package/docs/the-team.md +129 -0
  37. package/docs/troubleshooting.md +187 -0
  38. package/docs/upgrade.md +416 -0
  39. package/package.json +2 -1
@@ -0,0 +1,204 @@
1
+ # Deployment Guide
2
+
3
+ Run Flair on macOS, Linux, or Docker.
4
+
5
+ ## macOS (Apple Silicon)
6
+
7
+ ### Install
8
+
9
+ ```bash
10
+ npm install -g @tpsdev-ai/flair
11
+ flair init
12
+ ```
13
+
14
+ `flair init` will:
15
+ - Download Harper and the nomic-embed-text embedding model
16
+ - Create `~/.flair/` (config, data, keys)
17
+ - Generate admin credentials
18
+ - Install a launchd plist for auto-start on boot
19
+ - Start the server
20
+
21
+ ### Verify
22
+
23
+ ```bash
24
+ flair status
25
+ flair doctor
26
+ ```
27
+
28
+ ### Auto-start
29
+
30
+ `flair init` installs a launchd plist at `~/Library/LaunchAgents/ai.tpsdev.flair.plist`. Flair starts automatically on login and restarts if it crashes.
31
+
32
+ ```bash
33
+ # Manual control
34
+ flair stop
35
+ flair start
36
+ flair restart
37
+ ```
38
+
39
+ ### Port
40
+
41
+ Default port is `19926`. Override during init:
42
+
43
+ ```bash
44
+ flair init --port 8000
45
+ ```
46
+
47
+ Or edit `~/.flair/config.yaml` and restart.
48
+
49
+ ---
50
+
51
+ ## Linux
52
+
53
+ ### Prerequisites
54
+
55
+ - Node.js >= 22
56
+ - systemd (for auto-start)
57
+
58
+ ### Install
59
+
60
+ ```bash
61
+ npm install -g @tpsdev-ai/flair
62
+ flair init
63
+ ```
64
+
65
+ Same as macOS — detects the platform and generates a systemd unit file instead of a launchd plist.
66
+
67
+ ### Verify
68
+
69
+ ```bash
70
+ flair status
71
+ flair doctor
72
+ ```
73
+
74
+ ### Auto-start
75
+
76
+ The systemd unit file is installed at `~/.config/systemd/user/flair.service`.
77
+
78
+ ```bash
79
+ # Manual control
80
+ systemctl --user start flair
81
+ systemctl --user stop flair
82
+ systemctl --user restart flair
83
+
84
+ # View logs
85
+ journalctl --user -u flair -f
86
+ ```
87
+
88
+ ---
89
+
90
+ ## Docker
91
+
92
+ ### Quick test (from-scratch validation)
93
+
94
+ ```bash
95
+ cd docker/
96
+ ./test-from-scratch.sh
97
+ ```
98
+
99
+ This runs a clean install in a container — useful for verifying the install path works on a fresh machine.
100
+
101
+ ### Production Docker
102
+
103
+ ```dockerfile
104
+ FROM node:22-slim
105
+ RUN npm install -g @tpsdev-ai/flair
106
+ RUN flair init --skip-soul
107
+ EXPOSE 19926
108
+ CMD ["flair", "start", "--foreground"]
109
+ ```
110
+
111
+ Note: embeddings run on CPU in Docker (no Metal acceleration). Performance is acceptable for small-to-medium memory stores (< 10K memories).
112
+
113
+ ---
114
+
115
+ ## Harper Fabric
116
+
117
+ Deploying to a Harper Fabric cluster is a different mechanism from the installs above — `flair deploy` pushes Flair as a cluster component instead of `npm install -g`. To upgrade an already-deployed Fabric instance in place, use `FABRIC_USER=<admin> FABRIC_PASSWORD=<pass> flair upgrade --target <fabric-url>` (or `--fabric-password-file <path>` in place of the env var), not the local upgrade path. Inline `--fabric-user`/`--fabric-password` flags also work but are discouraged — both leak to shell history and `ps`. See [`docs/upgrade.md` — Upgrading a Fabric-deployed instance](upgrade.md#upgrading-a-fabric-deployed-instance) for the full walkthrough, including the automatic post-deploy fleet-convergence sweep.
118
+
119
+ ---
120
+
121
+ ## Remote Access
122
+
123
+ ### SSH tunnel (simplest)
124
+
125
+ ```bash
126
+ ssh -f -N -L 19926:localhost:19926 your-server
127
+ ```
128
+
129
+ Then set `FLAIR_URL=http://localhost:19926` on the client.
130
+
131
+ ### Direct network access
132
+
133
+ Edit `~/.flair/config.yaml`:
134
+
135
+ ```yaml
136
+ http:
137
+ port: 19926
138
+ host: 0.0.0.0 # listen on all interfaces
139
+ ```
140
+
141
+ **Security:** Flair uses Ed25519 authentication. Agents must present a valid signature to read or write. However, the `/Health` endpoint is unauthenticated. For internet-facing deployments, put Flair behind a reverse proxy with TLS.
142
+
143
+ ---
144
+
145
+ ## Configuration
146
+
147
+ All configuration lives in `~/.flair/`:
148
+
149
+ ```
150
+ ~/.flair/
151
+ ├── config.yaml # port, host, embedding model
152
+ ├── data/ # Harper database
153
+ ├── keys/ # Ed25519 keypairs per agent
154
+ └── backups/ # flair backup output
155
+ ```
156
+
157
+ ### Key config options (`~/.flair/config.yaml`)
158
+
159
+ ```yaml
160
+ http:
161
+ port: 19926 # API port (ops port = this - 1)
162
+ host: 127.0.0.1 # bind address
163
+
164
+ clustering:
165
+ nodeName: flair
166
+
167
+ logging:
168
+ level: warn
169
+ stdStreams: true
170
+ ```
171
+
172
+ ### Environment variables
173
+
174
+ Set these in the Flair process environment (`~/Library/LaunchAgents/ai.tpsdev.flair.plist` on macOS, the systemd unit on Linux, the component env on Fabric).
175
+
176
+ | Variable | What it does | When to set it |
177
+ |----------|--------------|----------------|
178
+ | `FLAIR_PUBLIC_URL` | The URL operators reach this Flair on (e.g. `https://flair.example.com`). Surfaced in the AdminInstance pane's Endpoints table and used by OAuth metadata + A2A discovery so external clients see a reachable URL. | **Always set on remote / Fabric / VPS deployments.** Local-only installs can leave it unset. |
179
+ | `HDB_ADMIN_PASSWORD` | Bootstrap password for the embedded Harper. After first start, the persisted user record is the source of truth; rotate via the Harper ops API, not by changing this env var. | Set at install time. See [secrets-and-keys.md](secrets-and-keys.md) for rotation. |
180
+ | `FLAIR_KEY_PASSPHRASE` | Passphrase used to derive the AES-256-GCM key that wraps federation private-key seeds at rest. Auto-generated to `~/.flair/keys/.passphrase` if unset. | Set explicitly for production federation deployments so the passphrase isn't auto-generated and lost on disk wipe. |
181
+ | `HTTP_PORT` | Override the Harper HTTP port. Useful for sandboxes; production deployments should configure the port in `config.yaml` instead. | Rare. |
182
+
183
+ ---
184
+
185
+ ## Backup & Restore
186
+
187
+ ```bash
188
+ # Backup all data (agents, memories, souls)
189
+ flair backup > ~/flair-backup-$(date +%Y%m%d).json
190
+
191
+ # Restore to a fresh instance
192
+ flair restore < ~/flair-backup-20260405.json
193
+ ```
194
+
195
+ Always backup before upgrades.
196
+
197
+ ---
198
+
199
+ ## Uninstall
200
+
201
+ ```bash
202
+ flair uninstall # stops server, removes ~/.flair/, removes launchd/systemd service
203
+ npm uninstall -g @tpsdev-ai/flair
204
+ ```
@@ -0,0 +1,109 @@
1
+ # Entity Vocabulary
2
+
3
+ The **attention plane** — an agent seeing what its teammates are actively touching, and
4
+ where its own work collides with theirs — needs one thing to work at all: a single,
5
+ consistent way to name the "things" (repos, issues, customers, subsystems, people, agents)
6
+ that memories, workspace state, org events, and relationships all reference. Without it,
7
+ "what touches entity E" can never be an indexed lookup — it degenerates into fuzzy string
8
+ matching across free-text fields.
9
+
10
+ This document is that convention. It's written down and enforced by a small validator
11
+ (`resources/entity-vocab.ts`) **before** anything is built on top of it, because three
12
+ surfaces already consume entity strings today (the `Relationship` graph's `subject`/`object`,
13
+ and the new `entities` fields on `WorkspaceState`/`OrgEvent`/`Memory`), plus a future
14
+ attention query and any future MCP scope field — retrofitting a divergent vocabulary later
15
+ is expensive.
16
+
17
+ > This doc covers the vocabulary and validator only — the foundation slice of the attention
18
+ > plane (flair#675). The attention query ("what touches entity E in the last N days") and
19
+ > bootstrap collision surfacing are separate, later slices. See `FLAIR-ATTENTION-PLANE.md`
20
+ > for the full design.
21
+
22
+ ## The form
23
+
24
+ An entity is a single string: `type:value`.
25
+
26
+ - **type** — lowercase, drawn from a **closed** set (below). Not user-extensible at write
27
+ time; new types are added deliberately, by changing `ENTITY_TYPES` in
28
+ `resources/entity-vocab.ts` (and this doc) in the same change.
29
+ - **value** — a stable identifier whose grammar depends on the type (see table).
30
+
31
+ Matching is **exact on the full string** — `repo:tpsdev-ai/flair` and
32
+ `repo:tpsdev-ai/flair-mcp` are unrelated entities, not prefix-related. This is what makes the
33
+ indexed `entities: [String] @indexed` fields a plain equality lookup rather than a scan.
34
+
35
+ ## The closed type set
36
+
37
+ | Type | Form | Example | Value grammar |
38
+ |------|------|---------|---------------|
39
+ | `repo` | `repo:<owner>/<name>` | `repo:tpsdev-ai/flair` | Two lowercase path segments (alnum + `.`/`-`/`_` internal separators) joined by exactly one `/`. |
40
+ | `issue` | `issue:<repo>#<n>` | `issue:tpsdev-ai/flair#504` | A valid `repo` value, then `#`, then a positive integer with no leading zero. |
41
+ | `customer` | `customer:<slug>` | `customer:acme` | A lowercase slug (alnum segments joined by single `-`/`_`). |
42
+ | `subsystem` | `subsystem:<slug>` | `subsystem:embeddings` | Same slug grammar as `customer`. |
43
+ | `agent` | `agent:<agentId>` | `agent:flint` | Same slug grammar — the agent's registered id. |
44
+ | `person` | `person:<id>` | `person:nathan` | Same slug grammar. |
45
+
46
+ Rules that apply across every type:
47
+
48
+ - The type prefix is **always lowercase** — `Repo:x`, `CUSTOMER:x` etc. are invalid (not a
49
+ case-insensitive match against the closed set — they simply aren't in it).
50
+ - No leading/trailing whitespace anywhere in the string.
51
+ - No empty type or empty value (`:acme`, `repo:`, `repo` with no colon are all invalid).
52
+ - Slugs never have leading/trailing/doubled separators (`-embeddings`, `embeddings-`,
53
+ `embed--dings` are all invalid).
54
+
55
+ ## The validator
56
+
57
+ `resources/entity-vocab.ts` is the single source of truth — every write path that persists
58
+ an `entities` value validates against it, not a local reimplementation of the grammar.
59
+
60
+ ```ts
61
+ import { isValidEntity, validateEntities, invalidEntitiesResponse } from "./entity-vocab.js";
62
+
63
+ isValidEntity("repo:tpsdev-ai/flair"); // true
64
+ isValidEntity("project:flair"); // false — "project" isn't in the closed type set
65
+ isValidEntity("Customer:Acme"); // false — uppercase type and value both reject
66
+
67
+ validateEntities(["repo:tpsdev-ai/flair", "bogus"]);
68
+ // => { valid: false, invalid: ["bogus"] }
69
+
70
+ validateEntities(undefined);
71
+ // => { valid: true, invalid: [] } — the field is additive/optional; absence is not an error
72
+ ```
73
+
74
+ `invalidEntitiesResponse(entities)` is a thin convenience wrapper for Harper resource write
75
+ paths: it returns a ready-to-return `400 { error: "invalid_entities", invalid: [...] }`
76
+ `Response` when validation fails, or `null` when the field is absent or every entry is valid.
77
+ `WorkspaceState.ts`, `OrgEvent.ts`, and `Memory.ts` all call it early in `post()`/`put()`.
78
+
79
+ ## Where `entities` lives today
80
+
81
+ Per the attention-plane spec's K&S-approved refinements, `entities: [String] @indexed` is an
82
+ **additive, nullable** field on:
83
+
84
+ - `WorkspaceState` (`schemas/workspace.graphql`)
85
+ - `OrgEvent` (`schemas/event.graphql`)
86
+ - `Memory` (`schemas/memory.graphql`) — added in v1 (not deferred to v2) for index-pushdown
87
+ uniformity across all three sources the future attention query joins.
88
+
89
+ Existing rows on all three tables simply carry no `entities` — readers must tolerate absence,
90
+ the same pattern already used for `Presence.activityUpdatedAt`. No migration, no backfill.
91
+
92
+ `Relationship` gets **no** `entities` field: its `subject`/`object` columns already carry
93
+ free-form entity-reference strings and are already indexed — they're the vocabulary carrier
94
+ for that table. They are lowercased on write today but not yet validated against this
95
+ vocabulary; wiring that validation is a follow-up, not part of this foundation slice.
96
+
97
+ ## What's explicitly NOT in this slice
98
+
99
+ - The attention query (`AttentionQuery` / `flair attention <entity>`) that joins Memory,
100
+ Relationship, WorkspaceState, Presence, and OrgEvent by entity — shipped in flair#678
101
+ (`resources/AttentionQuery.ts`), a later slice.
102
+ - Bootstrap collision surfacing ("others in the room") — shipped in flair#681
103
+ (`resources/collision-lib.ts` + `MemoryBootstrap.ts`'s "Others in the room" section), a
104
+ later slice.
105
+ - Automatic entity extraction/tagging on write (producers set `entities` themselves today,
106
+ where they choose to; there's no NLP-derived auto-population yet).
107
+ - Validating `Relationship.subject`/`object` against this vocabulary.
108
+
109
+ These are tracked as follow-ups in `FLAIR-ATTENTION-PLANE.md`.
@@ -0,0 +1,179 @@
1
+ # Federation
2
+
3
+ Hub-and-spoke sync between Flair instances. A hub instance coordinates sync for one or more spoke instances.
4
+
5
+ ## Overview
6
+
7
+ Federation lets multiple Flair instances share memories, relationships, and agent records. Each instance maintains its own Ed25519 identity. Sync requests are signed and verified against pinned peer public keys.
8
+
9
+ ```
10
+ Spoke A ──[signed sync]──▶ Hub ◀──[signed sync]── Spoke B
11
+ ```
12
+
13
+ - **Hub:** accepts sync pushes from paired spokes, can relay records between peers
14
+ - **Spoke:** pushes local changes to the hub, receives changes from other spokes via the hub
15
+
16
+ ## Pairing a New Spoke (Bootstrap-User Flow)
17
+
18
+ Pairing connects a spoke to a hub with mutual key pinning and an auth-aware handshake that works across all Harper topologies, including Harper Fabric.
19
+
20
+ ### Step-by-step
21
+
22
+ **1. Hub admin generates a pairing token triple**
23
+
24
+ On the hub machine, the admin runs `flair federation token`. The command emits a JSON triple containing a one-time bootstrap credential:
25
+
26
+ ```bash
27
+ flair federation token --admin-pass <hub-admin-password>
28
+ ```
29
+
30
+ Output (a single JSON object):
31
+
32
+ ```json
33
+ {"token":"<one-time-pairing-token>","user":"<bootstrap-username>","password":"<bootstrap-password>","expiresAt":"<ISO-8601-timestamp>"}
34
+ ```
35
+
36
+ Tokens expire after 60 minutes by default. Use `--ttl <minutes>` to adjust.
37
+
38
+ **2. Hub admin shares the triple with the spoke admin**
39
+
40
+ The JSON triple is shared out-of-band (secure file transfer, password manager, or similar). It must be stored as a plain JSON file on the spoke side or piped via stdin.
41
+
42
+ **3. Spoke admin runs the pair command**
43
+
44
+ On the spoke machine:
45
+
46
+ ```bash
47
+ # From a file
48
+ flair federation pair <hub-url> --token-from /path/to/triple.json
49
+
50
+ # From stdin
51
+ cat triple.json | flair federation pair <hub-url> --token-from -
52
+ ```
53
+
54
+ **4. Behind the scenes**
55
+
56
+ - The bootstrap user authenticates at the platform layer (works on standalone deployments and Harper Fabric alike).
57
+ - The resource handler validates the pairing token, the signed request body, and the binding between the bootstrap user and the token.
58
+ - On success the hub creates a `Peer` record for the spoke and removes the temporary bootstrap user. The spoke records the hub as its peer.
59
+
60
+ After pairing, both instances pin each other's Ed25519 public keys and are ready to sync.
61
+
62
+ ## Why the Bootstrap-User Flow?
63
+
64
+ Earlier designs relied on `allowCreate=true` combined with body-only authentication. That approach works on single-component deployments but breaks on Harper Fabric, where the platform authentication gate fires before the resource handler sees the request. The bootstrap-user flow (Option B) makes the pair handshake auth-aware so it operates correctly on all Harper topologies: standalone, Fabric single-node, and Fabric multi-node.
65
+
66
+ ## Fabric Pairing Example
67
+
68
+ When the hub runs on Harper Fabric, adapt the hub URL to the Fabric pattern:
69
+
70
+ ```bash
71
+ # 1. Hub admin generates the triple (on the Fabric host)
72
+ ssh hub-host
73
+ flair federation token --admin-pass <hub-admin-password> > /tmp/pair-triple.json
74
+
75
+ # 2. Transfer the triple to the spoke admin (out-of-band)
76
+ scp hub-host:/tmp/pair-triple.json ./pair-triple.json
77
+
78
+ # 3. Spoke admin pairs using the Fabric URL
79
+ flair federation pair https://<fabric-node>:19926/<instance-name> --token-from ./pair-triple.json
80
+ ```
81
+
82
+ Replace `<fabric-node>`, `<instance-name>`, and `<hub-admin-password>` with your actual values.
83
+
84
+ ## Sync
85
+
86
+ Push local changes to the hub:
87
+
88
+ ```bash
89
+ flair federation sync --admin-pass <password>
90
+ # Output: ✅ Synced 12 records (0 skipped) in 145ms
91
+ ```
92
+
93
+ ## Security
94
+
95
+ ### Signed requests
96
+
97
+ Every federation request (pair, sync) is signed with the sender's Ed25519 private key. The receiver verifies the signature against the peer's pinned public key. Unsigned or tampered requests are rejected with 401.
98
+
99
+ The signature covers the canonical JSON of the request body (keys sorted recursively, signature field excluded).
100
+
101
+ ### Encrypted key storage
102
+
103
+ Private key seeds are stored in `~/.flair/keys/<instanceId>.key`, encrypted with AES-256-GCM. The encryption key is derived via HKDF from:
104
+
105
+ 1. `FLAIR_KEY_PASSPHRASE` environment variable (recommended for production), or
106
+ 2. An auto-generated random passphrase at `~/.flair/keys/.passphrase` (mode 0600)
107
+
108
+ If neither the env var nor the passphrase file can be accessed, federation identity creation fails. Private keys are never stored in the database.
109
+
110
+ ### Pairing tokens
111
+
112
+ New peers must present a valid, unexpired, unused pairing token. This prevents unauthorized instances from joining the federation. Tokens are generated by the hub admin and are single-use.
113
+
114
+ Re-pairing an existing peer (same instance ID, same public key) does not require a token.
115
+
116
+ ### Originator enforcement
117
+
118
+ Spoke instances can only push records they originated. A spoke cannot overwrite records from another spoke or from the hub. The hub can relay records from any origin.
119
+
120
+ ### Timestamp ceiling
121
+
122
+ Records with `updatedAt` more than 5 minutes in the future are rejected. This prevents an attacker from using far-future timestamps to permanently win last-write-wins (LWW) merge conflicts.
123
+
124
+ ## CLI Reference
125
+
126
+ | Command | Description |
127
+ |---------|-------------|
128
+ | `flair federation status` | Show instance identity and peer connections |
129
+ | `flair federation pair <hub-url> --token-from <file>` | Pair this spoke with a hub using a token triple file (or `-` for stdin) |
130
+ | `flair federation sync` | Push local changes to the hub (one-shot) |
131
+ | `flair federation watch [--interval <s>]` | Run sync in a foreground daemon loop (default 30s) |
132
+ | `flair federation reachability` | Probe local instance + each paired peer (read-only) |
133
+ | `flair federation token [--ttl <min>]` | Generate a one-time pairing token triple (hub only) |
134
+
135
+ ## Conflict Resolution
136
+
137
+ Federation uses record-level last-write-wins (LWW) with ISO timestamp comparison. When two instances modify the same record, the one with the later `updatedAt` wins. Field-level LWW is planned for a future version.
138
+
139
+ ## Troubleshooting
140
+
141
+ ### config.yaml port drift
142
+
143
+ If the hub's configured port in `config.yaml` differs from the port the spoke is targeting, federation requests fail with a connection error. Verify the port matches between the spoke's pair URL and the hub's `config.yaml` (`federation.port` or the instance's listen port).
144
+
145
+ ```bash
146
+ # On the hub, confirm the listening port
147
+ grep -E 'port|federation' ~/.flair/config.yaml
148
+ ```
149
+
150
+ ### Local FederationInstance fetch needs auth
151
+
152
+ When troubleshooting on the hub, fetching `/federation/instances/<id>` locally (e.g. via `curl localhost`) may return a 401 if the request does not carry the authentication headers the platform layer expects. On Fabric this gate is enforced even on localhost. Use the CLI tooling (`flair federation status`) instead of raw HTTP calls for local inspection.
153
+
154
+ ### `flair_pair_initiator` role not found on hub
155
+
156
+ If pairing fails with a role-not-found error, the hub instance may be missing the `flair_pair_initiator` role. This role is created automatically during `flair init --remote` but can be lost if the database was reset or migrated manually. Re-run `flair init --remote` on the hub to restore default roles, then retry pairing.
157
+
158
+ ### Bootstrap user not deleted on Fabric
159
+
160
+ After a successful pairing, the temporary bootstrap user is automatically deleted. If it persists on a Fabric deployment, check that the hub's Harper operations log does not show a rollback or permission error during the cleanup step. Manually removing the stale bootstrap user via the Harper Studio is safe if needed — it is never used after pairing completes.
161
+
162
+ ### Stale Peer record on spoke
163
+
164
+ If a spoke was previously paired with a different hub (or the hub's identity key changed), the spoke may retain a stale `Peer` record pointing to the old hub. Remove the stale record before pairing with the new hub:
165
+
166
+ ```bash
167
+ # Show current peers
168
+ flair federation status
169
+
170
+ # Remove a specific peer (replace <instanceId>)
171
+ flair federation unpin <instanceId>
172
+ ```
173
+
174
+ ## Limitations (1.0)
175
+
176
+ - **HTTP push only** — no persistent WebSocket connections or real-time sync
177
+ - **Manual or watch-loop sync** — `flair federation sync` is one-shot; `flair federation watch --interval 30` runs it on a loop in a foreground daemon (wrap in launchd / systemd for production)
178
+ - **Single hub** — spoke-to-spoke sync goes through the hub
179
+ - **Record-level LWW** — not field-level; concurrent edits to different fields of the same record may lose data
@@ -0,0 +1,197 @@
1
+ # Flair integrations
2
+
3
+ Where Flair already runs. Each integration shown here is a working surface — the same memory, federated across all of them, scoped per-agent by Ed25519 keys.
4
+
5
+ > **The point.** Memory should follow the agent across orchestrators. Every entry below pulls from the same Flair instance, sees the same `agentId` namespace, respects the same isolation. Pick whichever harness you're shipping in; the memory layer doesn't care.
6
+
7
+ ---
8
+
9
+ ## Quick install matrix
10
+
11
+ | Surface | Install path | Auth | Notes |
12
+ |---------|--------------|------|-------|
13
+ | **Claude Code** | [`flair-mcp`](#claude-code-cursor-codex-gemini-cli-continuedev-via-flair-mcp) | MCP config | Standard MCP server |
14
+ | **Cursor** | [`flair-mcp`](#claude-code-cursor-codex-gemini-cli-continuedev-via-flair-mcp) | MCP config | Standard MCP server |
15
+ | **Continue.dev** | [`flair-mcp`](#claude-code-cursor-codex-gemini-cli-continuedev-via-flair-mcp) | MCP config | Standard MCP server |
16
+ | **OpenAI Codex CLI** | [`flair-mcp`](#claude-code-cursor-codex-gemini-cli-continuedev-via-flair-mcp) | MCP config | Standard MCP server |
17
+ | **Gemini CLI** | [`flair-mcp`](#claude-code-cursor-codex-gemini-cli-continuedev-via-flair-mcp) | MCP config | Standard MCP server |
18
+ | **Goose** (block/goose) | [`flair-mcp`](#claude-code-cursor-codex-gemini-cli-continuedev-via-flair-mcp) | MCP config | Goose ships native MCP support |
19
+ | **LangGraph (TS)** | [`langgraph-flair`](#langgraph-typescript) | FlairClient | Drop-in `BaseStore` |
20
+ | **OpenClaw** | [`openclaw-flair`](#openclaw) | Ed25519 | Native plugin + context engine |
21
+ | **n8n** | [`n8n-nodes-flair`](#n8n) | FlairApi credential | Three nodes (chat memory, search, store) |
22
+ | **Hermes Agent** | [`hermes-flair`](#hermes-agent) | Ed25519 | Python `MemoryProvider` |
23
+ | **Pi agent** | [`pi-flair`](#pi-agent) | Ed25519 | TS plugin |
24
+
25
+ Don't see your harness? If it speaks **MCP** — Flair already works with `flair-mcp`. If it has a **custom memory protocol** like LangGraph's `BaseStore` or CrewAI's `RAGStorage`, an adapter is a ~200-line package; [open an issue](https://github.com/tpsdev-ai/flair/issues) or [send a PR](https://github.com/tpsdev-ai/flair).
26
+
27
+ **Adjacent: memory bridges** — for moving memories between Flair and another memory product. Five bridges ship today (Mem0, ChatGPT exports, claude-project files, agentic-stack, markdown); see [bridges.md](bridges.md). Bridges are import/export plumbing, not live orchestrator integrations.
28
+
29
+ ---
30
+
31
+ ## Claude Code, Cursor, Codex, Gemini CLI, Continue.dev, Goose — via `flair-mcp`
32
+
33
+ [`@tpsdev-ai/flair-mcp`](https://www.npmjs.com/package/@tpsdev-ai/flair-mcp) is a [Model Context Protocol](https://modelcontextprotocol.io/) server that exposes Flair as a memory tool to any MCP-speaking client. One server, every MCP client.
34
+
35
+ No install step needed — every snippet below uses `npx -y @tpsdev-ai/flair-mcp`, which fetches and runs the server on demand (zero-install). The fastest path is `flair init`, which detects and wires these clients for you. To wire by hand, drop the relevant snippet into each tool's MCP config:
36
+
37
+ **Claude Code** (`~/.config/claude-code/config.toml` or per-project `.claude/config.toml`):
38
+ ```toml
39
+ [mcp.servers.flair]
40
+ command = "npx"
41
+ args = ["-y", "@tpsdev-ai/flair-mcp"]
42
+ env = { FLAIR_AGENT_ID = "claude-code" }
43
+ ```
44
+
45
+ **Cursor** (`~/.cursor/mcp.json`):
46
+ ```json
47
+ {
48
+ "mcpServers": {
49
+ "flair": {
50
+ "command": "npx",
51
+ "args": ["-y", "@tpsdev-ai/flair-mcp"],
52
+ "env": { "FLAIR_AGENT_ID": "cursor" }
53
+ }
54
+ }
55
+ }
56
+ ```
57
+
58
+ **Codex CLI** (`~/.codex/config.toml`):
59
+ ```toml
60
+ [mcp_servers.flair]
61
+ command = "npx"
62
+ args = ["-y", "@tpsdev-ai/flair-mcp"]
63
+
64
+ [mcp_servers.flair.env]
65
+ FLAIR_AGENT_ID = "codex"
66
+ ```
67
+
68
+ **Gemini CLI** (`~/.gemini/settings.json`): same shape as Cursor.
69
+
70
+ **Continue.dev** (`~/.continue/config.json`):
71
+ ```json
72
+ {
73
+ "experimental": {
74
+ "modelContextProtocolServer": {
75
+ "command": "npx",
76
+ "args": ["-y", "@tpsdev-ai/flair-mcp"],
77
+ "env": { "FLAIR_AGENT_ID": "continue" }
78
+ }
79
+ }
80
+ }
81
+ ```
82
+
83
+ **Goose** (`~/.config/goose/profiles.yaml`):
84
+ ```yaml
85
+ default:
86
+ extensions:
87
+ flair:
88
+ cmd: npx
89
+ args: ["-y", "@tpsdev-ai/flair-mcp"]
90
+ envs: { FLAIR_AGENT_ID: goose }
91
+ ```
92
+
93
+ **Auth.** Set `FLAIR_AGENT_ID` to whatever identifier you want this client to claim. The MCP server will prompt you to register that agent on first call (`flair agent add <id>` writes the Ed25519 keypair). Subsequent calls auto-load the key.
94
+
95
+ Full per-tool walkthrough including troubleshooting: [`docs/mcp-clients.md`](mcp-clients.md).
96
+
97
+ ---
98
+
99
+ ## LangGraph (TypeScript)
100
+
101
+ [`@tpsdev-ai/langgraph-flair`](https://www.npmjs.com/package/@tpsdev-ai/langgraph-flair) implements LangGraph's `BaseStore`. Drop-in for `InMemoryStore`.
102
+
103
+ ```bash
104
+ npm install @tpsdev-ai/langgraph-flair
105
+ ```
106
+
107
+ ```typescript
108
+ import { FlairStore } from "@tpsdev-ai/langgraph-flair";
109
+ import { createReactAgent } from "@langchain/langgraph/prebuilt";
110
+
111
+ const store = new FlairStore({ agentId: "my-langgraph-agent" });
112
+ const agent = createReactAgent({ llm, tools, store });
113
+ ```
114
+
115
+ Maps LangGraph namespaces to Flair tags, keys to ids, values to JSON content. Search delegates to Flair's HNSW. Filter operators applied client-side. Full mapping table: [`packages/langgraph-flair/README.md`](../packages/langgraph-flair/README.md).
116
+
117
+ LangGraph **Python** support is on the roadmap (same `BaseStore` shape, Python adapter).
118
+
119
+ ---
120
+
121
+ ## OpenClaw
122
+
123
+ [`@tpsdev-ai/openclaw-flair`](https://www.npmjs.com/package/@tpsdev-ai/openclaw-flair) is the native OpenClaw plugin. Adds Flair as a memory provider AND registers the `flair` context engine that re-injects PERMANENT-tier rules (SOUL.md, IDENTITY.md, AGENTS.md) every turn.
124
+
125
+ ```bash
126
+ openclaw plugins install @tpsdev-ai/openclaw-flair
127
+ ```
128
+
129
+ Configuration via OpenClaw's standard plugin surface. See [`docs/openclaw.md`](openclaw.md) for the per-agent install pattern, including how to wire SOUL.md so behavioral anchors persist across long sessions without drift.
130
+
131
+ ---
132
+
133
+ ## n8n
134
+
135
+ [`@tpsdev-ai/n8n-nodes-flair`](https://www.npmjs.com/package/@tpsdev-ai/n8n-nodes-flair) ships three nodes:
136
+
137
+ - **FlairChatMemory** — drop-in chat-memory for n8n's AI Agent / LangChain workflow nodes. Same role as Postgres / Redis chat memory but with cross-orchestrator portability.
138
+ - **FlairSearch** — semantic search over your Flair memories from any workflow.
139
+ - **FlairApi** credential — Ed25519 keypair entry point for the agentId.
140
+
141
+ Install via the standard n8n community-node UI (Settings → Community nodes → `@tpsdev-ai/n8n-nodes-flair`) or:
142
+
143
+ ```bash
144
+ cd ~/.n8n && npm install @tpsdev-ai/n8n-nodes-flair
145
+ ```
146
+
147
+ Walkthrough including a worked example flow: [`docs/n8n.md`](n8n.md).
148
+
149
+ ---
150
+
151
+ ## Hermes Agent
152
+
153
+ [`hermes-flair`](https://github.com/tpsdev-ai/flair/tree/main/packages/hermes-flair) implements Nous Research [Hermes](https://github.com/NousResearch/hermes-agent)'s `MemoryProvider` plugin contract in Python. Bootstrap injection at session start, background prefetch between turns, two tools (`flair_search`, `flair_store`), built-in MEMORY.md mirroring, circuit breaker.
154
+
155
+ ```bash
156
+ hermes plugins install path:/path/to/flair/packages/hermes-flair
157
+ ```
158
+
159
+ Auth: TPS-Ed25519 (the same model the rest of Flair uses) — writes are isolated per agent identity server-side; reads are open within the org for non-private memories, with `visibility: private` staying owner-only. See [SECURITY.md](../SECURITY.md).
160
+
161
+ ---
162
+
163
+ ## Pi agent
164
+
165
+ [`@tpsdev-ai/pi-flair`](https://www.npmjs.com/package/@tpsdev-ai/pi-flair) is the TS plugin for the [Pi coding agent](https://github.com/mariozechner/pi-coding-agent). Memory + identity for the Pi runtime.
166
+
167
+ ```bash
168
+ npm install @tpsdev-ai/pi-flair
169
+ ```
170
+
171
+ Pi resolves the plugin via its standard plugin config; pin `agentId` per host.
172
+
173
+ ---
174
+
175
+ ## Don't see your harness?
176
+
177
+ If it speaks MCP, you're already covered — every MCP client works through `flair-mcp` (the section above lists 6 we've explicitly tested).
178
+
179
+ If it has a custom memory protocol, the adapter pattern is small (~200 lines). LangGraph and Hermes are the reference implementations. **Adapters we'd love to see:**
180
+
181
+ - LangGraph Python (mirror of our TS adapter)
182
+ - CrewAI (Python `BaseRAGStorage` protocol)
183
+ - AG2 / AutoGen (Python)
184
+ - Mastra (TS, denser thread model)
185
+ - ADK (Google, Python + TS)
186
+
187
+ [Open an issue](https://github.com/tpsdev-ai/flair/issues) describing the harness and we'll triage. PRs welcome — see [`packages/langgraph-flair`](../packages/langgraph-flair) as the smallest-shape reference.
188
+
189
+ ---
190
+
191
+ ## See also
192
+
193
+ - [Quickstart](quickstart.md) — `flair init` to working memory in 30 seconds
194
+ - [Memory bridges](bridges.md) — import/export Flair ↔ Mem0, ChatGPT, claude-project, markdown, agentic-stack (five bridges shipped)
195
+ - [Federation](federation.md) — pair instances peer-to-peer for cross-machine sync
196
+ - [Supply-chain policy](supply-chain-policy.md) — what we do to keep this list of integrations safe
197
+ - [The team](the-team.md) — the multi-agent rig that builds Flair, dogfooded on every harness above