Skip to content

Commit 040c40e

Browse files
committed
brainkb skill: mandatory identity verification; PAT over ambient creds
Prevent silent wrong-user attribution: a login reporting 'Logged in as X' is not proof later calls run as X — the MCP can fall back to a configured BRAINKB_EMAIL/PASSWORD/TOKEN or header when a streamable-http session login isn't carried forward. Require brainkb_whoami() after login AND before every mutation/ingest, STOP on mismatch, and prefer a per-call PAT/header that can't be shadowed. Ingest section warns attribution is permanent.
1 parent ca18055 commit 040c40e

1 file changed

Lines changed: 40 additions & 0 deletions

File tree

skills/brainkb_skills/SKILL.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,23 @@ The base URL must be reachable **from wherever this code runs**:
7171
variable — do not paste it into chat. (The one exception is the PAT returned by
7272
`brainkb_create_token`, which is shown to the user **once** so they can copy it
7373
into their config — never re-display it afterward.)
74+
- **Identity must be verified, never assumed — this is the #1 correctness rule.**
75+
Mutations (create space, ingest, add member, grant) are **attributed to the
76+
authenticated identity permanently** (provenance records who did what). A login
77+
step reporting "Logged in as X" is **not proof** the next call runs as X: the MCP
78+
silently falls back to a configured `BRAINKB_EMAIL`/`BRAINKB_PASSWORD`,
79+
`BRAINKB_TOKEN`, or an `Authorization` header when the session login isn't
80+
carried forward (common on the hosted `streamable-http` transport). So:
81+
1. After **every** login, call `brainkb_whoami()` and confirm `email` == the
82+
intended user.
83+
2. **Immediately before any write/mutation**, call `brainkb_whoami()` again and
84+
confirm it still matches. Only proceed if it does.
85+
3. If it shows a **different/unexpected** account (e.g. a shared `test@…`),
86+
**STOP** — report the mismatch, don't write. The durable fix is a per-call
87+
credential that can't be shadowed: a **PAT** (`BRAINKB_TOKEN` /
88+
`brainkb_use_token`) or an `Authorization: Bearer` header. A leftover
89+
`BRAINKB_EMAIL`/`BRAINKB_PASSWORD` in the MCP config is the usual culprit and
90+
should be removed on a shared/multi-user MCP.
7491
- Confirm before mutating actions (creating a space, ingesting, changing
7592
visibility, adding members). Reads are safe.
7693
- **Authorization is role-based, not just JWT** (see the section below). A `403`
@@ -149,6 +166,21 @@ password unprompted:**
149166
says they have a password account and want to use it. Never prompt for a password
150167
otherwise.
151168

169+
> **⚠️ ALWAYS verify identity after logging in — do not trust the login message.**
170+
> Immediately after `brainkb_finish_login` / `brainkb_login` / `brainkb_use_token`,
171+
> call **`brainkb_whoami()`** and confirm the returned `email` is the account you
172+
> intended. A login step can report "Logged in as X" yet subsequent calls run as a
173+
> **different** account — because the MCP falls back, silently, to a configured
174+
> `BRAINKB_EMAIL`/`BRAINKB_PASSWORD` (or `BRAINKB_TOKEN`, or an `Authorization`
175+
> header) when the just-established session isn't carried into the next call (this
176+
> is common on the hosted `streamable-http` transport, where per-session login may
177+
> not persist between tool calls). **If `whoami` shows a different or unexpected
178+
> account, STOP — the login did not take effect. Do NOT create/ingest/mutate**
179+
> (see the identity rule in "Credentials & safety"). The reliable fix is a
180+
> per-call credential that can't be shadowed: a **PAT** via `BRAINKB_TOKEN` /
181+
> `brainkb_use_token`, or an `Authorization: Bearer` header — not an in-memory
182+
> session login on a multi-user remote.
183+
152184
### 2. Create / choose a workspace (space)
153185
- Individual/private workspace (any write-capable role):
154186
`brainkb_create_space(slug, name, "private", description)` (space_type defaults to
@@ -169,6 +201,14 @@ password unprompted:**
169201
- List what the user can see: `brainkb_list_spaces()`.
170202

171203
### 3. Ingest
204+
- **Before ingesting, verify identity: call `brainkb_whoami()` and confirm the
205+
`email` is the intended user.** Ingest is **attributed** — the job's `user_id`
206+
and the provenance (`prov:wasAssociatedWith` / `prov:wasAttributedTo`) are set to
207+
the authenticated identity, permanently. If `whoami` shows the wrong account
208+
(e.g. a fallback `test@…` from a stale `BRAINKB_EMAIL`), the data will be
209+
**mis-attributed and cannot be silently reassigned** — STOP and fix the login
210+
(use a PAT / header) before ingesting. Never ingest "hoping" the earlier login
211+
stuck.
172212
- Raw text: `brainkb_ingest_text(graph_iri, data)` (Turtle/N-Triples/JSON-LD).
173213
- Files: `brainkb_ingest_files(graph_iri, [paths])`.
174214
- Both return a `job_id`. Tell the user ingestion is running in the background.

0 commit comments

Comments
 (0)