Compare commits

..
2 Commits
Author SHA1 Message Date
mathiasandClaude Opus 5.5 0e2bd3d90e fix(mcp): audit record keeps legacy name/index alias targets
CD / Deploy via GitOps (push) Successful in 5s
CD / Lint / Test / Vet (push) Successful in 15s
CD / Build & Import (push) Successful in 7s
A caller using the #38 aliases (name for repo, index for number) left no
target in the tools/call audit record.

Refs #64

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 20:41:51 +02:00
mathiasandClaude Opus 5.5 b149dccdbe chore(context): re-sync adapters after root brain-scoping correction
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 20:41:47 +02:00
4 changed files with 27 additions and 3 deletions
+7 -1
View File
@@ -307,7 +307,13 @@ entries that age well are about *why*, *how to avoid*, and *what to do when*.
| **Anything HTTP-only (curl, scripts)** | `POST https://brain-mcp.d-ma.be/query` with `{"query":"..."}` (auth via `BRAIN_MCP_TOKEN`) | `POST .../write` with `{"content":"...","filename":"..."}` |
| **Browser / human inspection** | `https://git.d-ma.be/mathias/hyperguild` → `knowledge/` and `wiki/` markdown files |
- **Scoping**: defaults to `public` collection; client projects filter to `{client}` + `public`.
- **Scoping — the brain holds no confidential client content.** Nothing on the read path
filters by client: every token holder can read every wing, and `wing` on `brain_query`
is an optional argument, not an access control. Client confidentiality is enforced only
at write time (`capture`'s I1 gate refuses internal/confidential). So never `brain_write`
client material; generalise it first or keep it in the client's own repo. (hyperguild#144,
decided 2026-10-01. This line previously claimed client projects were filtered; they
were not.)
- **Routing**: brain_answer's LLM uses berget.ai as primary, iguana ollama as
fallback. Both are configurable in the `supervisor/ingestion-deployment.yaml`
on the koala k3s cluster; don't hardcode local-only model names into the
+7 -1
View File
@@ -302,7 +302,13 @@ entries that age well are about *why*, *how to avoid*, and *what to do when*.
| **Anything HTTP-only (curl, scripts)** | `POST https://brain-mcp.d-ma.be/query` with `{"query":"..."}` (auth via `BRAIN_MCP_TOKEN`) | `POST .../write` with `{"content":"...","filename":"..."}` |
| **Browser / human inspection** | `https://git.d-ma.be/mathias/hyperguild` → `knowledge/` and `wiki/` markdown files |
- **Scoping**: defaults to `public` collection; client projects filter to `{client}` + `public`.
- **Scoping — the brain holds no confidential client content.** Nothing on the read path
filters by client: every token holder can read every wing, and `wing` on `brain_query`
is an optional argument, not an access control. Client confidentiality is enforced only
at write time (`capture`'s I1 gate refuses internal/confidential). So never `brain_write`
client material; generalise it first or keep it in the client's own repo. (hyperguild#144,
decided 2026-10-01. This line previously claimed client projects were filtered; they
were not.)
- **Routing**: brain_answer's LLM uses berget.ai as primary, iguana ollama as
fallback. Both are configurable in the `supervisor/ingestion-deployment.yaml`
on the koala k3s cluster; don't hardcode local-only model names into the
+1 -1
View File
@@ -16,7 +16,7 @@ import (
// they identify what a call acted on without copying what it wrote. Every
// other argument (file content, issue bodies) is reduced to args_bytes so the
// audit trail never becomes a second copy of repo content (gitea-mcp#64).
var auditTargetKeys = []string{"owner", "repo", "number", "branch", "path", "tag"}
var auditTargetKeys = []string{"owner", "repo", "name", "number", "index", "branch", "path", "tag"}
// logToolCall emits one structured record per tools/call (OWASP MCP08).
func logToolCall(ctx context.Context, name string, args json.RawMessage, start time.Time, err error) {
+12
View File
@@ -121,3 +121,15 @@ func TestToolsCallAuditOmitsArgumentValuesOtherThanTarget(t *testing.T) {
assert.NotContains(t, string(raw), sentinel)
assert.Contains(t, recs[0], "args_bytes")
}
// #38 keeps `name` (for repo) and `index` (for number) as legacy aliases; a
// caller using them must still leave a target in the audit record.
func TestToolsCallAuditRecordsLegacyAliasTargets(t *testing.T) {
recs := captureAudit(t, func() {
callTool(t, auditServer(auditStubTool{name: "issue_close"}), "issue_close",
map[string]any{"owner": "mathias", "name": "infra", "index": 7})
})
require.Len(t, recs, 1)
assert.Equal(t, "infra", recs[0]["name"])
assert.EqualValues(t, 7, recs[0]["index"])
}