docs(agent/brain): add BRAIN-CALLERS.md living map
Records HTTP /v1/brain/* and in-process Brain caller contracts, recent hardening (#312, #1006, #998, #1052, #1055), and per-call-site request/ response shapes. Companion to docs/brain-callers-audit.md from #121. Recovers artefact lost when the original #179 worktree was cleaned up before cherry-pick landed on dev. Co-authored-by: Codex <noreply@openai.com> Closes tasks.lthn.sh/view.php?id=179
This commit is contained in:
parent
35b327d47e
commit
929f6effbd
1 changed files with 142 additions and 0 deletions
142
docs/BRAIN-CALLERS.md
Normal file
142
docs/BRAIN-CALLERS.md
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
<!-- SPDX-License-Identifier: EUPL-1.2 -->
|
||||
|
||||
# Brain API Callers
|
||||
|
||||
Date: 2026-04-25
|
||||
Ticket: Mantis #179
|
||||
Companion audit: `docs/brain-callers-audit.md` (broad sweep), this file is the focused living map for Brain callers and contracts.
|
||||
|
||||
## Purpose
|
||||
|
||||
This document records who calls Brain APIs in this workspace, which endpoint or in-process action they use, what protections sit on that path, and what request/response shape each caller expects.
|
||||
|
||||
Future Brain call sites should be added here in the same change that introduces them.
|
||||
|
||||
## Recent Hardening To Preserve
|
||||
|
||||
- `#312` org auth: `php/Services/BrainService.php` rejects unauthorised org scopes before remember, recall, and forget proceed.
|
||||
- `#1006` org bounds: PHP service and MCP schemas bound org values to 128 characters.
|
||||
- `#998` `brain.key` perms: the shared Go client writes `~/.claude/brain.key` as `0600` and rejects insecure existing permissions.
|
||||
- `#1052` bearer SSRF: the shared Go client rejects absolute request URLs and only appends relative `/v1/brain/*` paths to the configured API base URL.
|
||||
- `#1055` retry jitter: the shared Go client retries `408`, `429`, and `5xx` with `Retry-After` support plus full-jitter backoff and a circuit breaker.
|
||||
|
||||
## Canonical Contract Surfaces
|
||||
|
||||
### HTTP `/v1/brain/*`
|
||||
|
||||
| Endpoint | Current request shape | Current success shape | Current error shape | Notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `POST /v1/brain/remember` | `content`, `type`, `tags?`, `project?`, `confidence?`, `supersedes?`, `expires_in?` | `201 {"data": <memory>}` | `422 {"error":"validation_error","message":...}`, `503 {"error":"service_error","message":...}` | The controller currently does not validate or forward `org`, so external HTTP callers cannot rely on org-scoped remember yet. |
|
||||
| `POST /v1/brain/recall` | `query`, `limit?`, `top_k?`, `org?`, `project?`, `type?`, `keywords?`, `boost_keywords?`, `filter?` | `200 {"data":{"memories":[...],"scores":{...},"count":n}}` | `422 {"error":"validation_error","message":...}`, `503 {"error":"service_error","message":...}` | This is the current HTTP route that actually models org-aware recall. |
|
||||
| `DELETE /v1/brain/forget/{id}` | path `id`, optional JSON `reason` | `200 {"data": {...}}` | `404 {"error":"not_found","message":...}`, `503 {"error":"service_error","message":...}` | Forget runs through workspace and org checks in `ForgetKnowledge` and `BrainService`. |
|
||||
| `GET /v1/brain/list` | `project?`, `type?`, `agent_id?`, `limit?` | `200 {"data":{"memories":[...],"count":n}}` | `422 {"error":"validation_error","message":...}` | The controller currently does not validate `org`, even though the PHP MCP tool and shared Go client both model org-filtered list calls. |
|
||||
| `GET /v1/brain/search` | `q`, `org?`, `project?`, `limit?` | `200 {"data":{"memories":[...],"count":n}}` | `503 {"error":"service_error","message":...}` | Search is PHP-only in this repo; no Go caller was found here. |
|
||||
| `GET /v1/brain/tags` | none | `200 {"data": {"tag": count}}` | `503 {"error":"service_error","message":...}` | PHP-only read endpoint over Elasticsearch aggregates. |
|
||||
| `GET /v1/brain/scopes` | none | `200 {"data": {"org":{"project":count}}}` | `503 {"error":"service_error","message":...}` | PHP-only read endpoint over Elasticsearch aggregates. |
|
||||
|
||||
### In-process PHP service and actions
|
||||
|
||||
`php/Services/BrainService.php` is the canonical PHP write/read implementation behind the controller, MCP tools, console commands, and Livewire explorer.
|
||||
|
||||
- Remember input shape:
|
||||
`workspace_id`, `agent_id`, `type`, `content`, `tags?`, `org?`, `project?`, `confidence?`, `supersedes_id?`, `expires_at?`, `source?`
|
||||
- Recall input shape:
|
||||
`query`, `topK`, `filter`, `workspaceId`, optional `keywords`, optional `boostKeywords`
|
||||
- Remember/recall protections:
|
||||
workspace-scoped persistence, org-scope authorisation, bounded retry against Qdrant and Elasticsearch, `Retry-After` support, capped retry delay (`30s`), and queue-based embedding/index cleanup
|
||||
- Forget protections:
|
||||
workspace ownership check, org-scope authorisation, DB delete plus async index cleanup
|
||||
|
||||
### Shared Go client
|
||||
|
||||
The canonical Go client lives in module `dappco.re/go/mcp/pkg/mcp/brain/client`, which resolves to `../mcp/pkg/mcp/brain/client/client.go` in this workspace via `go.mod`.
|
||||
|
||||
- Typed inputs:
|
||||
`RememberInput`, `RecallInput`, `ForgetInput`, `ListInput`
|
||||
- Success surface:
|
||||
parsed `map[string]any`
|
||||
- Error surface:
|
||||
non-2xx becomes an error; the upstream JSON body is preserved inside the error text, not returned as a parsed map
|
||||
- Protections:
|
||||
Bearer auth, default org and agent injection on typed calls, `https` enforcement unless `CORE_BRAIN_INSECURE=true`, `brain.key` `0600` enforcement, absolute-URL rejection, `408/429/5xx` retry, `Retry-After` handling, full-jitter backoff, circuit breaker
|
||||
|
||||
## PHP Callers
|
||||
|
||||
### HTTP controller
|
||||
|
||||
| Call site | Endpoint(s) | Protections | Input shape | Output shape / notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `php/Controllers/Api/BrainController.php` | `remember`, `recall`, `forget`, `list`, `search`, `tags`, `scopes` | `AgentApiAuth` permission checks (`brain.read` or `brain.write`), Bearer auth, workspace binding from API key, rate-limit headers, downstream org auth in `BrainService` | Route-specific JSON and query validation; see HTTP contract table above | Returns wrapped JSON under `data` on success. `remember` and `list` are not yet fully aligned with the org-aware service/client contract. |
|
||||
|
||||
### MCP tools
|
||||
|
||||
| Call site | Endpoint / action | Protections | Input shape | Output shape / notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `php/Mcp/Tools/Agent/Brain/BrainRemember.php` | in-process `RememberKnowledge::run()` | workspace context dependency, MCP circuit breaker wrapper, org max length `128`, service-level org auth and retry | `content`, `type`, `tags?`, `org?`, `project?`, `confidence?`, `supersedes?`, `expires_in?` | `{"success":true,"memory":{...}}` |
|
||||
| `php/Mcp/Tools/Agent/Brain/BrainRecall.php` | in-process `RecallKnowledge::run()` | workspace context dependency, MCP circuit breaker wrapper, org max length `128`, service-level org auth and retry | `query`, `top_k?`, `filter?` where filter can include `org`, `project`, `type`, `agent_id`, `min_confidence` | `{"success":true,"count":n,"memories":[...],"scores":{...}}` |
|
||||
| `php/Mcp/Tools/Agent/Brain/BrainList.php` | in-process `BrainMemory` query | workspace context dependency, no external HTTP, org max length `128` | `org?`, `project?`, `type?`, `agent_id?`, `limit?` | `{"success":true,"memories":[...],"count":n}` |
|
||||
| `php/Mcp/Tools/Agent/Brain/BrainForget.php` | in-process `ForgetKnowledge::run()` | workspace context dependency, MCP circuit breaker wrapper, service-level org auth | `id`, `reason?` | `{"success":true,...}` |
|
||||
|
||||
### Console and UI callers
|
||||
|
||||
| Call site | Endpoint / action | Protections | Input shape | Output shape / notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `php/Console/Commands/BrainSeedMemoryCommand.php` | direct `BrainService::remember()` | service validation, org auth, retry, queued indexing | `workspace_id`, `agent_id`, `type`, `content`, `tags`, `project`, `confidence` | Side-effect only; logs imported/skipped counts |
|
||||
| `php/Console/Commands/BrainIngestCommand.php` | direct `BrainService::remember()` | same as above | same remember shape plus optional `source` | Side-effect only; truncates oversized text before embedding |
|
||||
| `php/Console/Commands/PrepWorkspaceCommand.php` | direct `BrainService::recall()` | service validation, org auth, retry | repo query plus optional issue query, `topK`, filters, `workspaceId` | Builds `CONTEXT.md` from `memories` and `scores` |
|
||||
| `php/Agentic/Livewire/BrainExplorer.php` | `RecallKnowledge::run()`, `ListKnowledge::run()`, `ForgetKnowledge::run()` | workspace validation, service/org checks, DB fallback on recall failure | query plus list/recall filters | UI-normalised memory cards; fallback search if vector recall fails |
|
||||
|
||||
## Go Callers
|
||||
|
||||
### Shared client consumers in this repo
|
||||
|
||||
| Call site | Endpoint(s) | Protections | Input shape | Output shape / notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `pkg/agentic/brain_client.go` | arbitrary relative `/v1/brain/*` via shared client `Call()` | shared Go client protections plus helper-level `CORE_BRAIN_ORG` injection when body omits `org`; subsystem-scoped shared circuit breaker | raw `map[string]any` body | Returns `core.Result`; callers use `brainPayloadMap()` to unwrap `data` when present |
|
||||
| `pkg/agentic/prep.go` | `POST /v1/brain/recall` | helper above | `query`, `top_k`, `project`, `agent_id`; helper can inject `org` | Reads `data.memories` to build workspace context |
|
||||
| `pkg/agentic/session.go` | `POST /v1/brain/remember` | helper above | `content`, `agent_id`, `type`, `tags`, `confidence`, optional `project` | Best-effort session handoff persistence; failures are logged and ignored |
|
||||
| `pkg/agentic/brain_seed_memory.go` | `POST /v1/brain/remember` | helper above | `workspace_id`, `agent_id`, `type`, `content`, `tags`, `project`, `confidence` | Best-effort import; failures are counted as skipped |
|
||||
|
||||
### Go MCP brain subsystem
|
||||
|
||||
| Call site | Endpoint(s) | Protections | Input shape | Output shape / notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `pkg/brain/direct.go` | `POST /v1/brain/remember`, `POST /v1/brain/recall`, `DELETE /v1/brain/forget/{id}`, `GET /v1/brain/list` | shared Go client protections, env-based default org, `~/.claude/brain.key` fallback, Bearer auth | remember: `content`, `type`, `tags?`, `org?`, `project?`, `confidence?`, `supersedes?`, `expires_in?`, `agent_id`; recall: `query`, `top_k`, optional org/project/type/agent_id/min_confidence`; list: query params `org`, `project`, `type`, `agent_id`, `limit` | Exposes MCP tool responses such as `RememberOutput{Success, MemoryID}` and `RecallOutput{Count, Memories}` |
|
||||
|
||||
## Scripts And Plugin Callers
|
||||
|
||||
### Python plugins
|
||||
|
||||
| Call site | Endpoint(s) | Protections | Input shape | Output shape / notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `hermes/plugins/openbrain_memory.py` | `remember`, `recall`, `forget`, `list` | Bearer auth header, optional default `org`, optional default `workspace_id`, async background write dispatch for turn sync | remember/list/recall/forget payloads are forwarded largely as-is after empty-value cleanup | Returns decoded JSON plus `status`; no shared breaker, no shared retry/jitter, no absolute-URL guard |
|
||||
| `hermes/plugins/openbrain_context.py` | `POST /v1/brain/recall` | Bearer auth header, default `workspace_id`, default `org` in `filter` | `{"query":..., "top_k":..., "filter":{"workspace_id":...,"org":...}}` | Accepts several response layouts (`data.memories`, `results`, `items`, `matches`) and normalises candidates locally; no shared breaker or retry |
|
||||
|
||||
### Shell scripts
|
||||
|
||||
| Call site | Endpoint(s) | Protections | Input shape | Output shape / notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `claude/core/scripts/session-start.sh` | `POST /v1/brain/recall` | Bearer auth header, loads `~/.claude/brain.key`, short `curl --max-time` | raw JSON body with `query`, `top_k`, `agent_id`, optional inline `project` or `type` fragments | Parses JSON on stdout; no shared org injection, no retry, no breaker, no SSRF guard |
|
||||
| `claude/core/scripts/session-save.sh` | `POST /v1/brain/remember` | Bearer auth header, `brain.key` fallback, debounce before write | raw JSON body with `content`, `type`, `project`, `agent_id`, `tags` | Fire-and-forget autosave; no org, no retry, no breaker |
|
||||
| `claude/core/scripts/pre-compact.sh` | `POST /v1/brain/remember` | Bearer auth header, `brain.key` fallback | raw JSON body with `content`, `type`, `project`, `agent_id`, `tags` | Fire-and-forget compaction snapshot; no org, no retry, no breaker |
|
||||
|
||||
## Non-runtime References
|
||||
|
||||
- `plugins/core-go/skills/api-endpoints/SKILL.md`
|
||||
- `plugins/core-php/skills/api-endpoints/SKILL.md`
|
||||
|
||||
These are documentation/examples only. They are not runtime callers, but they can still become copy-paste bypasses if they drift away from the hardened shared-client path.
|
||||
|
||||
## Contract-Test Follow-up For Part B
|
||||
|
||||
Part B was not implemented in this lane because the current HTTP controller surface is not yet fully aligned with the service and shared-client contract that the test needs to lock down.
|
||||
|
||||
- `POST /v1/brain/remember` currently drops `org` at controller validation time, so a PHP endpoint test cannot truthfully assert the same org-aware remember contract that the service and Go client model.
|
||||
- `GET /v1/brain/list` currently omits `org` from controller validation even though the PHP MCP tool and shared Go client both model org-filtered list requests.
|
||||
- The shared Go client correctly preserves upstream error JSON inside the error text, but it does not currently expose non-2xx bodies as parsed structured data, so an "identical error shape" assertion needs either a small shared wrapper or a raw HTTP harness.
|
||||
|
||||
Recommended follow-up before adding the cross-runtime contract test:
|
||||
|
||||
1. Align `BrainController::remember()` with the org-aware remember contract.
|
||||
2. Align `BrainController::list()` with the org-aware list contract.
|
||||
3. Add a PHP route-level Pest test and a Go shared-client integration test that both use the same `remember(core)` and `remember(evil)` fixtures once the HTTP contract is aligned.
|
||||
Loading…
Add table
Reference in a new issue