6 Commits
Author SHA1 Message Date
mathiasandClaude Opus 5.5 a6da93332d feat(brain): audit-log every tools/call; server-owned write provenance (#147)
CI / Lint / Test / Vet (push) Successful in 39s
CI / Mirror to GitHub (push) Successful in 4s
Closes #144 (audit line per tools/call) and #145 (origin/written_by stamped on every model-reachable write, surfaced in brain_query/brain_get).

Passed pr-gatekeeper after four rounds of adversarial review, verified by execution: #147

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Mathias <mthbqv@gmail.com>
2026-10-01 21:06:36 +00:00
mathias 3d5af85713 fix(frontmatter): consolidate 3 independent fence-parsers into one (#139)
CI / Lint / Test / Vet (push) Successful in 32s
CI / Mirror to GitHub (push) Successful in 4s
Co-authored-by: Mathias <mthbqv@gmail.com>
2026-09-23 13:07:32 +00:00
mathiasandClaude Sonnet 5 e157311cd8 fix(mcp): validate the claim envelope before brain_edit writes too (#136)
CI / Lint / Test / Vet (push) Successful in 39s
CI / Mirror to GitHub (push) Successful in 4s
7 review rounds closed 8 distinct bypasses across two mechanism families: fence-detection (field-name substring, block-containment, fence-stripping, CRLF, UTF-8 BOM) and value-extraction (folded YAML block scalar hiding an invalid value). A known v1 gap remains in the block-scalar fold's handling of blank-line-interrupted continuations — documented on checkClaimEnvelope, accepted deliberately (narrow, requires an already-privileged caller deliberately constructing a specific malformed-YAML shape) rather than chased past diminishing returns. Real fix (shared, tested, encoding-normalized frontmatter parser) tracked separately as hyperguild#138.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Mathias <mthbqv@gmail.com>
2026-09-23 10:28:07 +00:00
mathias 5a5d938e8e Brain-quality sweep, batch 1: brain_edit/brain_update correctness + close-session docs (#124)
CI / Lint / Test / Vet (push) Successful in 32s
CI / Mirror to GitHub (push) Successful in 4s
Co-authored-by: Mathias <mthbqv@gmail.com>
2026-09-22 21:27:11 +00:00
mathias 36a45068ab feat(mcp): record and alert on a governance-file rewrite (#97) (#98)
CI / Lint / Test / Vet (push) Successful in 1m23s
CI / Mirror to GitHub (push) Successful in 4s
brain_edit_root was gated but unobserved: on success it emitted a slog.Warn
and nothing else. That proves a rewrite was allowed, not that one happened —
and brain-sync commits it within 15 minutes authored `brain-sync`, so git
history cannot tell a CLAUDE.md rewrite apart from routine note traffic.

A successful brain_edit_root now emits an ntfy notification and an AuditEntry
through the same sink a capture already uses. notes.Result gains
PrevContentHash so both ends of the change are identifiable.

Path and hashes only in the notification body. Reserved at Internal, not
Confidential, because Confidential makes DegradingSink refuse to record while
loki is down rather than buffer. Observation is best-effort and never fails
the call; a failure to observe logs at ERROR.

Ordinary brain_edit on knowledge//wiki/ is deliberately not audited — git
history already carries the same before/after bytes with a diff.

Closes #97.

Co-authored-by: Mathias <mthbqv@gmail.com>
2026-08-22 06:09:53 +00:00
mathias 055af368cd feat(mcp): brain_read + brain_edit, contained to the sync source (brain#4) (#96)
CI / Lint / Test / Vet (push) Successful in 29s
CI / Mirror to GitHub (push) Skipped
brain_read returns a note verbatim; brain_edit is str_replace with an
exactly-once match, operating on the tree brain-sync commits from.

An adversarial design review before implementation changed three things:

1. Governance files are not in the model-facing tool. Brain content is
   ingested from the outside world and reaches a model's context, and that
   model would hold the editor — one poisoned page could rewrite the
   CLAUDE.md a .dispatch-allow agent later loads as instructions. And
   brain-sync merges with --strategy-option=ours, so the live tree beats a
   human's corrective push on the conflicting lines. CLAUDE.md now goes
   through brain_edit_root: de-surfaced from tools/list and gated on the
   local static principal. The scopes are disjoint — the privileged door
   has the smaller reach.

2. Reads needed the same allowlist, and brain_get already lacked it: it
   returned verbatim session transcripts under archive/ by path. A test
   proved the exposure live before the fix.

3. Canonicalise before allowlisting — knowledge/../.git/hooks/post-commit
   would otherwise reach a hook that brain-sync executes in a pod holding
   the Gitea push credentials.

Plus overlapping match counting, UTF-8 validation both ways, refusal while
git is mid-operation, flock with the read inside the lock, atomic
same-dir rename, and reindex only when frontmatter is touched.

Refs brain#4.

Co-authored-by: Mathias <mthbqv@gmail.com>
2026-08-18 18:14:05 +00:00