@tpsdev-ai/flair 0.26.0 → 0.27.1
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.
- package/README.md +2 -0
- package/dist/cli.js +434 -50
- package/dist/doctor-client.js +47 -0
- package/dist/resources/migrations/embedding-stamp.js +83 -2
- package/dist/resources/migrations/runner.js +44 -0
- package/docs/assets/flair-cross-orchestrator.cast +33 -0
- package/docs/assets/flair-cross-orchestrator.gif +0 -0
- package/docs/assets/flair-demo.cast +18 -0
- package/docs/assets/flair-demo.gif +0 -0
- package/docs/auth.md +178 -0
- package/docs/bridges.md +291 -0
- package/docs/claude-code.md +202 -0
- package/docs/deployment.md +204 -0
- package/docs/entity-vocabulary.md +109 -0
- package/docs/federation.md +179 -0
- package/docs/integrations.md +197 -0
- package/docs/mcp-clients.md +240 -0
- package/docs/n8n-management.md +142 -0
- package/docs/n8n.md +122 -0
- package/docs/notes/adk-spike-findings-2026-05-05.md +331 -0
- package/docs/notes/mcp-agent-auth-consumer.md +229 -0
- package/docs/notes/mcp-oauth-model2.md +132 -0
- package/docs/notes/rem-ux.md +39 -0
- package/docs/openclaw.md +131 -0
- package/docs/quickstart.md +153 -0
- package/docs/releasing.md +173 -0
- package/docs/rem.md +60 -0
- package/docs/rerank-provisioning.md +67 -0
- package/docs/secrets-and-keys.md +173 -0
- package/docs/spoke-bringup.md +303 -0
- package/docs/supply-chain-policy.md +156 -0
- package/docs/system-requirements.md +42 -0
- package/docs/the-team.md +129 -0
- package/docs/troubleshooting.md +187 -0
- package/docs/upgrade.md +416 -0
- package/package.json +2 -1
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# Flair via MCP — Claude Code, Gemini CLI, OpenAI Codex CLI
|
|
2
|
+
|
|
3
|
+
Flair ships an MCP server (`@tpsdev-ai/flair-mcp`) that any MCP-compatible client can use as its persistent memory + identity layer. One server, three (and counting) integrations. Switch between agent CLIs without losing your agent's memory.
|
|
4
|
+
|
|
5
|
+
This page is the install + config snippet for each of the three major CLIs. The bootstrap is the same:
|
|
6
|
+
|
|
7
|
+
1. Install Flair and create an agent identity (one-time, ~2 min).
|
|
8
|
+
2. Add the MCP server to your CLI of choice (1 command or 1 file).
|
|
9
|
+
3. Verify the agent can call `memory_search` / `memory_store`.
|
|
10
|
+
|
|
11
|
+
If you've never set up Flair before, do step 1 first. If Flair is already running and you have an agent ID, jump to your CLI section.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Step 1 — Install Flair (do once)
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Install Flair globally
|
|
19
|
+
npm install -g @tpsdev-ai/flair
|
|
20
|
+
|
|
21
|
+
# Initialize the local Harper-backed server
|
|
22
|
+
flair init
|
|
23
|
+
|
|
24
|
+
# Provision an agent identity. Pick a name — typically per-project, per-purpose,
|
|
25
|
+
# or "me" if you want one durable identity across everything.
|
|
26
|
+
flair agent add my-project
|
|
27
|
+
# → writes ~/.flair/keys/my-project.key (Ed25519 PKCS8) and registers the agent
|
|
28
|
+
|
|
29
|
+
# Sanity check
|
|
30
|
+
flair status
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Flair runs as a local server at `http://127.0.0.1:19926` by default. The MCP server connects to it on demand via Ed25519-signed requests; nothing leaves your machine unless you explicitly route to a remote Flair instance.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Step 2 — Wire the MCP server into your CLI
|
|
38
|
+
|
|
39
|
+
Pick whichever you use. The MCP server is the same package; only the config syntax differs.
|
|
40
|
+
|
|
41
|
+
### Claude Code
|
|
42
|
+
|
|
43
|
+
The canonical approach is the `claude mcp add` CLI (writes to `~/.claude/mcp.json`):
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
claude mcp add flair --scope user \
|
|
47
|
+
-e FLAIR_AGENT_ID=my-project \
|
|
48
|
+
-- npx -y @tpsdev-ai/flair-mcp
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Verify:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
claude mcp list
|
|
55
|
+
# → flair (stdio, npx -y @tpsdev-ai/flair-mcp)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Or, if you prefer the project-scoped `.mcp.json` checked into your repo:
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"mcpServers": {
|
|
63
|
+
"flair": {
|
|
64
|
+
"command": "npx",
|
|
65
|
+
"args": ["-y", "@tpsdev-ai/flair-mcp"],
|
|
66
|
+
"env": {
|
|
67
|
+
"FLAIR_AGENT_ID": "my-project"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
#### Auto-recall on session start (optional hook)
|
|
75
|
+
|
|
76
|
+
The MCP server gives the agent *pull* access to memory — it calls `bootstrap` /
|
|
77
|
+
`memory_search` when it decides to. If you'd rather have Flair context loaded
|
|
78
|
+
automatically the moment a session opens (no "call the bootstrap tool" nudge),
|
|
79
|
+
register Flair's `SessionStart` hook. It's a separate bin shipped in the same
|
|
80
|
+
package and is entirely optional — it complements the MCP server, it doesn't
|
|
81
|
+
replace it.
|
|
82
|
+
|
|
83
|
+
The one-command way (recommended — idempotent, `--dry-run`-able, and
|
|
84
|
+
symmetric with `flair hook uninstall`/`flair hook status`):
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
flair hook install # wires ~/.claude/settings.json for FLAIR_AGENT_ID/FLAIR_URL
|
|
88
|
+
flair hook install --dry-run # prints the exact JSON delta, writes nothing
|
|
89
|
+
flair hook status # wired? correct shape? which agent/instance?
|
|
90
|
+
flair hook uninstall # removes only Flair's hook entry
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`--harness claude-code` is the only supported value today (it's also the
|
|
94
|
+
default) — the flag exists so a future harness is an additive registry entry,
|
|
95
|
+
not a breaking change. `flair doctor` already checks for this same hook (see
|
|
96
|
+
below) and recognizes anything `flair hook install` writes.
|
|
97
|
+
|
|
98
|
+
Or wire it by hand — add a `SessionStart` hook to `~/.claude/settings.json`:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"hooks": {
|
|
103
|
+
"SessionStart": [
|
|
104
|
+
{
|
|
105
|
+
"hooks": [
|
|
106
|
+
{
|
|
107
|
+
"type": "command",
|
|
108
|
+
"command": "FLAIR_AGENT_ID=me npx -y @tpsdev-ai/flair-mcp flair-session-start"
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Swap `me` for your `FLAIR_AGENT_ID`. The hook reads Claude Code's SessionStart
|
|
118
|
+
payload on stdin, calls Flair's `bootstrap` (soul + relevant memories +
|
|
119
|
+
predicted context, scoped to your project by the session's working directory),
|
|
120
|
+
and emits it as `hookSpecificOutput.additionalContext` — which Claude Code
|
|
121
|
+
injects into the new session's context. The matcher is omitted, so it fires on
|
|
122
|
+
every session start (`startup`, `resume`, `clear`, `compact`); add
|
|
123
|
+
`"matcher": "startup"` to a hook group if you only want it on fresh sessions.
|
|
124
|
+
|
|
125
|
+
It honors the same env as the MCP server (`FLAIR_AGENT_ID`, `FLAIR_URL`,
|
|
126
|
+
`FLAIR_KEY_PATH`), plus `FLAIR_HOOK_TIMEOUT_MS` (default 8000, clamped
|
|
127
|
+
500–30000) for the bootstrap timeout.
|
|
128
|
+
|
|
129
|
+
**It degrades to a no-op, always.** No `FLAIR_AGENT_ID`, Flair down, an auth
|
|
130
|
+
error, or a hung daemon (past the timeout) → the hook prints `{}` and exits 0.
|
|
131
|
+
Claude Code treats that as "no context to add" and starts normally. The hook
|
|
132
|
+
can never block or break session startup. The injected context is clamped to
|
|
133
|
+
≤10,000 characters to keep the session-start payload small.
|
|
134
|
+
|
|
135
|
+
### Gemini CLI
|
|
136
|
+
|
|
137
|
+
Edit `~/.gemini/settings.json` (create it if absent):
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"mcpServers": {
|
|
142
|
+
"flair": {
|
|
143
|
+
"command": "npx",
|
|
144
|
+
"args": ["-y", "@tpsdev-ai/flair-mcp"],
|
|
145
|
+
"env": {
|
|
146
|
+
"FLAIR_AGENT_ID": "my-project"
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Restart your Gemini CLI session for the config to take effect. Then in chat:
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
> @flair memory_search "what did we decide about auth last week?"
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### OpenAI Codex CLI
|
|
160
|
+
|
|
161
|
+
Edit `~/.codex/config.toml` (create it if absent):
|
|
162
|
+
|
|
163
|
+
```toml
|
|
164
|
+
[mcp_servers.flair]
|
|
165
|
+
command = "npx"
|
|
166
|
+
args = ["-y", "@tpsdev-ai/flair-mcp"]
|
|
167
|
+
|
|
168
|
+
[mcp_servers.flair.env]
|
|
169
|
+
FLAIR_AGENT_ID = "my-project"
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
For project-scoped trust (per Codex's MCP guide), the same block in `.codex/config.toml` at the project root.
|
|
173
|
+
|
|
174
|
+
Restart your Codex CLI session and the `flair_*` tools become available to the agent.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Step 3 — Verify
|
|
179
|
+
|
|
180
|
+
In any of the three CLIs, ask the agent to do this:
|
|
181
|
+
|
|
182
|
+
> Use the bootstrap tool to load my Flair memory context, then store a memory that says "successful first MCP integration test."
|
|
183
|
+
|
|
184
|
+
If you see (a) the agent calling the `bootstrap` tool returning soul + recent memories, and (b) `memory_store` confirming a write — you're wired up. The memory now persists across CLI sessions AND across CLIs. Switch to a different CLI tomorrow and `memory_search "MCP integration test"` will find it.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## What the MCP server exposes
|
|
189
|
+
|
|
190
|
+
Eleven tools, kept deliberately small:
|
|
191
|
+
|
|
192
|
+
| Tool | What it does |
|
|
193
|
+
|---|---|
|
|
194
|
+
| `memory_search` | Semantic search across your agent's memories |
|
|
195
|
+
| `memory_store` | Save a memory with type, durability, tags. Auto-dedups near-duplicates |
|
|
196
|
+
| `memory_update` | Update an existing memory by ID — overwrite in place, or version it with `preserveHistory` |
|
|
197
|
+
| `memory_get` | Fetch a specific memory by ID |
|
|
198
|
+
| `memory_delete` | Remove a memory |
|
|
199
|
+
| `relationship_store` | Record a subject-predicate-object relationship triple (e.g. "nathan manages flair") |
|
|
200
|
+
| `bootstrap` | Get session-start context: soul + recent memories + predicted-relevant context |
|
|
201
|
+
| `soul_set` | Set a personality/project/standards entry — included in every bootstrap |
|
|
202
|
+
| `soul_get` | Get a soul entry |
|
|
203
|
+
| `flair_workspace_set` | Set your agent's current workspace state (ref/branch, phase, task) in the Office Space |
|
|
204
|
+
| `flair_orgevent` | Publish an org-wide coordination event (claim/release/status) to the Office Space |
|
|
205
|
+
|
|
206
|
+
Writes are scoped per-agent (your `FLAIR_AGENT_ID`) and enforced by Flair's server, not by client convention — you can't write as another agent. Reads are more open by design: any agent on the same Flair instance can read any other agent's non-private memories (open-within-org read; see [SECURITY.md](../SECURITY.md)). Mark a memory `visibility: private` to keep it owner-only.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Configuration reference
|
|
211
|
+
|
|
212
|
+
| Env var | Default | Notes |
|
|
213
|
+
|---|---|---|
|
|
214
|
+
| `FLAIR_AGENT_ID` | (none — required) | Must match `flair agent add <id>` |
|
|
215
|
+
| `FLAIR_URL` | `http://127.0.0.1:19926` | Override for remote Flair instances |
|
|
216
|
+
| `FLAIR_KEY_PATH` | `~/.flair/keys/<agent>.key` | Ed25519 PKCS8 key — created by `flair agent add` |
|
|
217
|
+
|
|
218
|
+
The MCP server has no client-side flags beyond these env vars; everything else (timeouts, dedup thresholds, error classification) is opinionated defaults from the underlying [`@tpsdev-ai/flair-client`](../packages/flair-client) package.
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## What about Hermes (Nous Research)?
|
|
223
|
+
|
|
224
|
+
Hermes uses its own Python-native `MemoryProvider` ABC instead of MCP. It has its own Flair integration in [`packages/hermes-flair/`](../packages/hermes-flair). Same backend, same agent isolation, different plug shape.
|
|
225
|
+
|
|
226
|
+
Future MCP-capable agent CLIs (and there are more landing every month) will work out of the box with the MCP server above — no per-framework adapter required from us.
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Troubleshooting
|
|
231
|
+
|
|
232
|
+
**"FLAIR_AGENT_ID is required" on startup.** Set it in the MCP server's `env` block (per snippets above). The CLI's own env doesn't propagate to the spawned MCP subprocess unless declared.
|
|
233
|
+
|
|
234
|
+
**"connection_error: could not reach Flair at http://127.0.0.1:19926".** The Flair server isn't running. Run `flair status` to check; `flair start` to bring it up.
|
|
235
|
+
|
|
236
|
+
**"auth_error: …" on every call.** The agent identity doesn't match a registered key. Re-run `flair agent add <id>` (idempotent on re-add — won't lose existing memories).
|
|
237
|
+
|
|
238
|
+
**Tool calls succeed but the agent doesn't see results in subsequent turns.** Check that the CLI is actually invoking `bootstrap` at session start — most CLIs need an explicit prompt nudge ("call the bootstrap tool now") on first use. Subsequent turns should pick up automatically once the CLI sees the schema.
|
|
239
|
+
|
|
240
|
+
For deeper issues see [`troubleshooting.md`](troubleshooting.md) and the [`@tpsdev-ai/flair-mcp` repo](https://github.com/tpsdev-ai/flair/tree/main/packages/flair-mcp).
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# n8n Workflow Management
|
|
2
|
+
|
|
3
|
+
Programmatic activation/deactivation of n8n workflows — the approach we use for ops automation.
|
|
4
|
+
|
|
5
|
+
## The Problem
|
|
6
|
+
|
|
7
|
+
Direct SQLite edits to `workflow_entity.active` (or using `n8n update:workflow` while n8n is running) don't re-register schedule triggers in the in-memory scheduler. The schedule simply stops firing. Only the v1 public API's `POST /api/v1/workflows/:id/activate` (or flipping Active in the UI) properly registers the trigger.
|
|
8
|
+
|
|
9
|
+
**Root cause**: n8n tracks activation via `activeVersionId`, not the `active` boolean alone. The `activeWorkflowManager` in-memory scheduler only picks up workflows where `activeVersionId IS NOT NULL`. Direct SQLite edits that only set `active=1` without setting `activeVersionId` will not be picked up even on restart.
|
|
10
|
+
|
|
11
|
+
## Recommended Approach: v1 Public API (live instance)
|
|
12
|
+
|
|
13
|
+
The **v1 public API** is the correct tool for managing workflows on a **running** n8n instance. It updates the DB AND calls the in-memory `ActiveWorkflowManager.add()` to register triggers immediately — no restart needed.
|
|
14
|
+
|
|
15
|
+
### Endpoints
|
|
16
|
+
|
|
17
|
+
| Action | Method | Endpoint | Body |
|
|
18
|
+
|---|---|---|---|
|
|
19
|
+
| Activate | `POST` | `/api/v1/workflows/:id/activate` | `{}` or `{"versionId": "..."}` |
|
|
20
|
+
| Deactivate | `POST` | `/api/v1/workflows/:id/deactivate` | (none) |
|
|
21
|
+
| List | `GET` | `/api/v1/workflows` | — |
|
|
22
|
+
| Get one | `GET` | `/api/v1/workflows/:id` | — |
|
|
23
|
+
|
|
24
|
+
### Authentication
|
|
25
|
+
|
|
26
|
+
All v1 API calls require `X-N8N-API-KEY` header with a valid API key (see [API Key Generation](#api-key-generation) below).
|
|
27
|
+
|
|
28
|
+
### Examples
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Activate workflow
|
|
32
|
+
curl -X POST "http://127.0.0.1:5678/api/v1/workflows/$WORKFLOW_ID/activate" \
|
|
33
|
+
-H "X-N8N-API-KEY: $N8N_API_KEY" \
|
|
34
|
+
-H "Content-Type: application/json" \
|
|
35
|
+
-d '{}'
|
|
36
|
+
|
|
37
|
+
# Deactivate workflow
|
|
38
|
+
curl -X POST "http://127.0.0.1:5678/api/v1/workflows/$WORKFLOW_ID/deactivate" \
|
|
39
|
+
-H "X-N8N-API-KEY: $N8N_API_KEY"
|
|
40
|
+
|
|
41
|
+
# Check if workflow is active
|
|
42
|
+
curl -s "http://127.0.0.1:5678/api/v1/workflows/$WORKFLOW_ID" \
|
|
43
|
+
-H "X-N8N-API-KEY: $N8N_API_KEY" | python3 -c "import json,sys; print(json.load(sys.stdin)['active'])"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
See `scripts/n8n/activate-workflow.sh` for a reusable wrapper.
|
|
47
|
+
|
|
48
|
+
## Why PATCH Doesn't Work
|
|
49
|
+
|
|
50
|
+
n8n's v1 API docs mention `PATCH /api/v1/workflows/:id` with `{ "active": true }`. **This returns 405 Method Not Allowed on n8n 1.123.38.** The documented activation endpoints are `POST /api/v1/workflows/:id/activate` and `POST /api/v1/workflows/:id/deactivate`.
|
|
51
|
+
|
|
52
|
+
## Alternative Approaches
|
|
53
|
+
|
|
54
|
+
### Approach 2: CLI when n8n is stopped
|
|
55
|
+
|
|
56
|
+
The `n8n update:workflow` CLI command works when n8n is **not running**. It updates both `active` and `activeVersionId` in the DB. On the next `n8n start`, `ActiveWorkflowManager.init()` reads `getAllActiveIds()` and registers all workflows with non-null `activeVersionId`.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Stop n8n
|
|
60
|
+
launchctl stop ai.tpsdev.n8n
|
|
61
|
+
|
|
62
|
+
# Activate/deactivate via CLI
|
|
63
|
+
N8N_PATH="/opt/homebrew/opt/node@24/bin" npx n8n@1.123.38 update:workflow --id=$WORKFLOW_ID --active=true
|
|
64
|
+
# or
|
|
65
|
+
N8N_PATH="/opt/homebrew/opt/node@24/bin" npx n8n@1.123.38 update:workflow --id=$WORKFLOW_ID --active=false
|
|
66
|
+
|
|
67
|
+
# Start n8n
|
|
68
|
+
launchctl start ai.tpsdev.n8n
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Caveats**:
|
|
72
|
+
- `update:workflow` is deprecated (n8n v2 will replace it with `publish:workflow`/`unpublish:workflow`)
|
|
73
|
+
- Requires a full stop/start cycle (downtime for all workflows)
|
|
74
|
+
- `npx` needs node@24 (`/opt/homebrew/opt/node@24/bin/node`)
|
|
75
|
+
|
|
76
|
+
### Approach 3: Internal REST API (editor UI)
|
|
77
|
+
|
|
78
|
+
The `/rest/workflows/:id/activate` endpoint is what the editor UI uses. It requires session-based auth (cookie from `/rest/login`), which needs the admin password. Not practical for automation without storing credentials.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Requires session cookie from /rest/login
|
|
82
|
+
curl -X POST "http://127.0.0.1:5678/rest/workflows/$WORKFLOW_ID/activate" \
|
|
83
|
+
-b "fern-uid=...; n8n.session=..."
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Not recommended** for ops automation — session cookies expire and the password is a higher-value secret than an API key.
|
|
87
|
+
|
|
88
|
+
## API Key Generation
|
|
89
|
+
|
|
90
|
+
The v1 API key is a JWT signed with a secret derived from the encryption key. To create one programmatically:
|
|
91
|
+
|
|
92
|
+
```javascript
|
|
93
|
+
const crypto = require('crypto');
|
|
94
|
+
const jwt = require('jsonwebtoken');
|
|
95
|
+
const fs = require('fs');
|
|
96
|
+
|
|
97
|
+
// Read encryption key from n8n config
|
|
98
|
+
const config = JSON.parse(fs.readFileSync(process.env.HOME + '/.n8n/config', 'utf8'));
|
|
99
|
+
const encKey = config.encryptionKey;
|
|
100
|
+
|
|
101
|
+
// Derive JWT secret: SHA256 of every-other-character of encryption key
|
|
102
|
+
let baseKey = '';
|
|
103
|
+
for (let i = 0; i < encKey.length; i += 2) {
|
|
104
|
+
baseKey += encKey[i];
|
|
105
|
+
}
|
|
106
|
+
const jwtSecret = crypto.createHash('sha256').update(baseKey).digest('hex');
|
|
107
|
+
|
|
108
|
+
// Sign JWT with user's ID (from `user` table in SQLite)
|
|
109
|
+
const userId = '<user-uuid-from-sqlite>'; // e.g., 'a6ce7ac9-ef6d-4157-835a-4b0f01dc788a'
|
|
110
|
+
const apiKey = jwt.sign(
|
|
111
|
+
{ sub: userId, iss: 'n8n', aud: 'public-api' },
|
|
112
|
+
jwtSecret
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
// Store the returned key in the user_api_keys table so it persists
|
|
116
|
+
|
|
117
|
+
console.log('API key:', apiKey);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Once stored in the DB, the key is valid until deleted. No expiration by default.
|
|
121
|
+
|
|
122
|
+
**Security note**: The encryption key is needed to derive the JWT secret. This is the same key used to decrypt workflow credentials. If the encryption key is compromised, so are the credentials. Keep it scoped to the n8n user.
|
|
123
|
+
|
|
124
|
+
## Why Direct SQLite Edits Don't Work
|
|
125
|
+
|
|
126
|
+
The `workflow_entity` table has two activation fields:
|
|
127
|
+
- `active` (boolean): UI flag
|
|
128
|
+
- `activeVersionId` (varchar): points to the `workflow_history` record that defines the active version
|
|
129
|
+
|
|
130
|
+
The `ActiveWorkflowManager` only considers workflows where `activeVersionId IS NOT NULL`. If you only flip `active=1` without setting `activeVersionId`, n8n never picks up the workflow — not on restart, not on refresh.
|
|
131
|
+
|
|
132
|
+
Even with both fields set correctly, a live n8n instance has an in-memory scheduler that doesn't re-read the DB. Only the v1 API (or UI toggle) calls `activeWorkflowManager.add()` to register the trigger in-memory.
|
|
133
|
+
|
|
134
|
+
## Quick Reference
|
|
135
|
+
|
|
136
|
+
| Scenario | Method | Restart needed? |
|
|
137
|
+
|---|---|---|
|
|
138
|
+
| Activate on running n8n | `POST /api/v1/workflows/:id/activate` | No |
|
|
139
|
+
| Deactivate on running n8n | `POST /api/v1/workflows/:id/deactivate` | No |
|
|
140
|
+
| Activate when stopped | `n8n update:workflow --id=X --active=true` | Yes |
|
|
141
|
+
| Direct DB edit (active + activeVersionId) | SQLite `UPDATE` | Yes, on restart |
|
|
142
|
+
| Direct DB edit (active only) | SQLite `UPDATE` | **Won't work** — needs activeVersionId too |
|
package/docs/n8n.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Flair + n8n
|
|
2
|
+
|
|
3
|
+
Use Flair as the memory backend for n8n's AI Agent. The same memories are readable from Claude Code, OpenClaw, and any other Flair client — that's the point.
|
|
4
|
+
|
|
5
|
+
## When to use Flair vs n8n's built-in memory connectors
|
|
6
|
+
|
|
7
|
+
n8n ships memory connectors for Postgres, MongoDB, and Redis. Those are real, persistent, and work fine for **conversation-buffer** use cases inside a single n8n instance.
|
|
8
|
+
|
|
9
|
+
Flair is the right pick when you want:
|
|
10
|
+
|
|
11
|
+
| | Flair | n8n built-ins |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| **Shape** | Tagged + typed memories with semantic search, plus chat-buffer compatibility | Conversation-buffer only (LangChain `BaseMessage` records) |
|
|
14
|
+
| **Cross-orchestrator** | Same memory readable from Claude Code, OpenClaw, n8n | n8n-internal schema; nothing else reads it |
|
|
15
|
+
| **Cross-instance** | Hub-spoke federation built-in (local ↔ Fabric, etc.) | Single-instance unless you self-build replication |
|
|
16
|
+
| **Identity** | Ed25519 per-agent (planned) or admin-token (v1) | n8n credential per workflow |
|
|
17
|
+
|
|
18
|
+
If your AI Agent only needs to remember the last N turns of a single chat in a single n8n instance, Postgres-as-memory is fine. If you want the same memory to inform a Claude Code conversation tomorrow, or to persist across n8n redeploys via federated Flair, this package is the path.
|
|
19
|
+
|
|
20
|
+
## Setup (5 minutes)
|
|
21
|
+
|
|
22
|
+
### 1. Install Flair (if not already running)
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install -g @tpsdev-ai/flair
|
|
26
|
+
flair init
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This starts a local Flair server on `http://localhost:19926`. For shared/team setups, see [Deployment](./deployment.md).
|
|
30
|
+
|
|
31
|
+
### 2. Install the n8n community node
|
|
32
|
+
|
|
33
|
+
In n8n: **Settings → Community Nodes → Install** → enter `@tpsdev-ai/n8n-nodes-flair` → confirm and restart.
|
|
34
|
+
|
|
35
|
+
### 3. Create the credential
|
|
36
|
+
|
|
37
|
+
In n8n: **Credentials → New → Flair API**. Fill in:
|
|
38
|
+
|
|
39
|
+
| Field | Value |
|
|
40
|
+
|---|---|
|
|
41
|
+
| **Base URL** | `http://localhost:19926` (or your team's Flair URL) |
|
|
42
|
+
| **Agent ID** | Logical identity that will own memories from this n8n workspace. Workflows that share an Agent ID share memory ownership. Use distinct IDs when isolation matters. |
|
|
43
|
+
| **Admin Password** | Your Flair admin password (in `~/.flair/admin-pass` for local installs). **Sensitive** — see [Security](#security). |
|
|
44
|
+
|
|
45
|
+
Click **Test** — if the test request to `/Memory` returns 200, the credential is good.
|
|
46
|
+
|
|
47
|
+
### 4. Wire the nodes
|
|
48
|
+
|
|
49
|
+
Three nodes ship in the package:
|
|
50
|
+
|
|
51
|
+
- **Flair Chat Memory** — connects to an AI Agent's `Memory` socket. Stores chat history in Flair, scoped by Subject. Defaults to per-workflow memory; set the optional Session Sub-Key to `={{ $execution.id }}` for per-run isolation.
|
|
52
|
+
- **Flair Search** — connects to an AI Agent's `Tool` socket. Two operations:
|
|
53
|
+
- *Semantic Search* — agent calls `flair_search({ query })`, gets memories ranked by similarity.
|
|
54
|
+
- *Get By Subject* — agent calls `flair_get_by_subject()`, gets memories under a config-time-bound subject.
|
|
55
|
+
- **Flair Write** — a regular Main-input/Main-output pipeline node (not wired to the AI Agent's Tool socket). Takes an incoming item and writes its content as a Flair memory, with Subject, Tags, and Durability fields. For operator-driven capture-and-archive flows (mail → memory, webhook → memory, parsed-doc → memory) where the write is the workflow author's choice, not the LLM's.
|
|
56
|
+
|
|
57
|
+
A typical workflow:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
[Webhook] → [AI Agent]
|
|
61
|
+
├─ Model: Claude / OpenAI / etc.
|
|
62
|
+
├─ Memory: Flair Chat Memory (Subject: customer-support)
|
|
63
|
+
├─ Tool: Flair Search (Operation: Semantic Search)
|
|
64
|
+
└─ Tool: HTTP Request (etc.)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The agent now answers using both its current chat history (from Flair Chat Memory) and any relevant historical memories it pulls in via Flair Search.
|
|
68
|
+
|
|
69
|
+
## Subject and SessionId guidance
|
|
70
|
+
|
|
71
|
+
n8n memory connectors expose a `sessionKey` parameter that scopes the chat history. Flair has a richer model:
|
|
72
|
+
|
|
73
|
+
- **Subject** (required) — the entity / conversation / topic the memory is about. Indexed in Flair's schema; efficient to filter on. Default: `={{ $workflow.name }}`.
|
|
74
|
+
- **Session Sub-Key** (optional) — appended to the subject as `<subject>:<sessionKey>`. Use the n8n execution id (`={{ $execution.id }}`) for per-run isolation, or a customer/user id for per-customer scoping, or leave blank to share across runs.
|
|
75
|
+
|
|
76
|
+
Patterns:
|
|
77
|
+
|
|
78
|
+
- **"This assistant remembers"** — set Subject to a stable string (`customer-support`, `daily-standup`). Leave Session Sub-Key blank. All runs share memory.
|
|
79
|
+
- **Per-conversation isolation** — set Subject to the conversation owner (`customer:1234`), leave Session Sub-Key blank. Each conversation is isolated by subject.
|
|
80
|
+
- **Per-execution isolation** — set Session Sub-Key to `={{ $execution.id }}`. Each n8n run gets its own memory window. (This is most similar to n8n's default `sessionKey={{ $execution.id }}`.)
|
|
81
|
+
|
|
82
|
+
## Security
|
|
83
|
+
|
|
84
|
+
> **The admin password gives every workflow with this credential read/write access to the entire Flair instance**, not just the configured Agent ID. The blast radius is the whole memory store. Treat the credential as highly sensitive: n8n encrypts credentials at rest, but any n8n admin or backup restore can extract it.
|
|
85
|
+
|
|
86
|
+
For production deployments where untrusted workflow inputs reach Flair, wait for **Ed25519 per-agent authentication** (planned). v1 (admin password) is appropriate when:
|
|
87
|
+
|
|
88
|
+
- The n8n instance is single-tenant and operator-controlled
|
|
89
|
+
- Workflow inputs are trusted (your own CRM, your own webhook source)
|
|
90
|
+
- Memory leakage between agents is acceptable for the use case
|
|
91
|
+
|
|
92
|
+
If any of those don't hold, use Flair's CLI / SDK clients (which support per-agent Ed25519 today) and wait for the n8n credential update.
|
|
93
|
+
|
|
94
|
+
## Get By Tag — coming soon
|
|
95
|
+
|
|
96
|
+
The Flair Search node currently exposes Semantic Search and Get By Subject. **Get By Tag** is deferred until `flair-client.memory.list` exposes a `tags` filter (tracked in the [n8n-node spec](https://github.com/tpsdev-ai/flair/blob/main/specs/N8N-NODE-q3qf.md) §6). Workaround for now: use Semantic Search and let the model filter results by tags in the response.
|
|
97
|
+
|
|
98
|
+
## Worked examples
|
|
99
|
+
|
|
100
|
+
Two example workflows are coming in a follow-up release; they're authored inside a real n8n instance and round-tripped via Export so they import cleanly:
|
|
101
|
+
|
|
102
|
+
- **`chat-memory-demo.json`** — Webhook → AI Agent (Claude + Flair Chat Memory) → Respond. Demonstrates the conversation-buffer use case. Run twice with the same input to see memory replay.
|
|
103
|
+
- **`knowledge-search-demo.json`** — Schedule → AI Agent (Claude + Flair Chat Memory + Flair Search as Tool) → action. Demonstrates the structured-knowledge-search use case.
|
|
104
|
+
|
|
105
|
+
In the interim, follow the [Setup](#setup-5-minutes) and [Subject and SessionId guidance](#subject-and-sessionid-guidance) sections — wiring is straightforward without an example file.
|
|
106
|
+
|
|
107
|
+
## Compared to other Flair surfaces
|
|
108
|
+
|
|
109
|
+
| Surface | Use case | Setup |
|
|
110
|
+
|---|---|---|
|
|
111
|
+
| [Claude Code](./claude-code.md) | Personal AI assistant memory across CLI sessions | npm install + `flair init` |
|
|
112
|
+
| [OpenClaw](./openclaw.md) | Multi-agent OpenClaw deployments | OpenClaw plugin install |
|
|
113
|
+
| [MCP](./mcp-clients.md) | Any MCP client (Claude Desktop, etc.) | MCP server registration |
|
|
114
|
+
| **n8n (this doc)** | Workflow-engine AI Agents | n8n community-node install |
|
|
115
|
+
|
|
116
|
+
Same Flair instance, same memories, different surfaces.
|
|
117
|
+
|
|
118
|
+
## See also
|
|
119
|
+
|
|
120
|
+
- [Spec — `@tpsdev-ai/n8n-nodes-flair`](https://github.com/tpsdev-ai/flair/blob/main/specs/N8N-NODE-q3qf.md) — implementation plan, design decisions, anti-patterns
|
|
121
|
+
- [Bridges](./bridges.md) — how Flair memories flow between hosts and instances
|
|
122
|
+
- [Federation](./federation.md) — hub-and-spoke replication
|