feat(brain): stamp server-owned write provenance; surface it in brain_query
brain_write was an ungated, durable prompt-injection path: a session that just read an untrusted page could write an entry every later session retrieves as trusted guidance (OWASP MCP06). Decision (#145): tag provenance rather than stage writes. Every write and update stamps origin (static | jwt | unknown) and written_by (the authenticated principal) into frontmatter. Server-owned: origin/written_by in caller content or an update fragment are discarded, so a caller cannot claim a more trusted origin. No principal stamps origin: unknown, never silence. brain_update re-stamps to the last writer. capture (the claude.ai connector door) stamps too, via a new server-derived CaptureContext.AuthPath set by both capture doors after decode. REST POST /write carries no principal and stamps unknown. brain_query results carry origin/written_by when the note has them; older notes omit them rather than fabricate. brain_get already returns frontmatter. Refs #145 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -48,6 +48,11 @@ type Result struct {
|
||||
// (infra#72). Read from frontmatter when present, otherwise
|
||||
// inferred from the parent directory.
|
||||
Tier string `json:"tier,omitempty"`
|
||||
// Origin/WrittenBy surface write provenance (hyperguild#145) so a
|
||||
// reader can discount an entry a connector session wrote. Omitted for
|
||||
// notes written before provenance existed — never fabricated.
|
||||
Origin string `json:"origin,omitempty"`
|
||||
WrittenBy string `json:"written_by,omitempty"`
|
||||
}
|
||||
|
||||
// tierWeight maps the DIKW tier to a score multiplier applied right
|
||||
@@ -146,6 +151,7 @@ func QueryContext(ctx context.Context, brainDir string, opts QueryOptions) ([]Re
|
||||
rel = filepath.ToSlash(rel)
|
||||
wing, hall := extractWingHall(string(content), rel)
|
||||
tier := extractTier(string(content), rel)
|
||||
origin, writtenBy := extractProvenance(string(content))
|
||||
results = append(results, Result{
|
||||
Path: rel,
|
||||
Title: extractTitle(string(content), d.Name()),
|
||||
@@ -154,6 +160,9 @@ func QueryContext(ctx context.Context, brainDir string, opts QueryOptions) ([]Re
|
||||
Wing: wing,
|
||||
Hall: hall,
|
||||
Tier: tier,
|
||||
|
||||
Origin: origin,
|
||||
WrittenBy: writtenBy,
|
||||
})
|
||||
return nil
|
||||
})
|
||||
@@ -272,6 +281,7 @@ func hydrate(brainDir, relPath string) (Result, error) {
|
||||
}
|
||||
wing, hall := extractWingHall(string(content), relPath)
|
||||
tier := extractTier(string(content), relPath)
|
||||
origin, writtenBy := extractProvenance(string(content))
|
||||
return Result{
|
||||
Path: relPath,
|
||||
Title: extractTitle(string(content), filepath.Base(relPath)),
|
||||
@@ -279,9 +289,45 @@ func hydrate(brainDir, relPath string) (Result, error) {
|
||||
Wing: wing,
|
||||
Hall: hall,
|
||||
Tier: tier,
|
||||
|
||||
Origin: origin,
|
||||
WrittenBy: writtenBy,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// extractProvenance reads origin/written_by from the leading frontmatter
|
||||
// block only — a body line that happens to start with "origin:" is not
|
||||
// provenance.
|
||||
func extractProvenance(content string) (origin, writtenBy string) {
|
||||
scanner := bufio.NewScanner(strings.NewReader(content))
|
||||
inFrontmatter := false
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.TrimSpace(line) == "---" {
|
||||
if !inFrontmatter {
|
||||
inFrontmatter = true
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
if !inFrontmatter {
|
||||
return "", ""
|
||||
}
|
||||
key, val, ok := strings.Cut(line, ":")
|
||||
if !ok || strings.HasPrefix(line, " ") || strings.HasPrefix(line, "\t") {
|
||||
continue
|
||||
}
|
||||
v := strings.Trim(strings.TrimSpace(val), `"'`)
|
||||
switch strings.TrimSpace(key) {
|
||||
case "origin":
|
||||
origin = v
|
||||
case "written_by":
|
||||
writtenBy = v
|
||||
}
|
||||
}
|
||||
return origin, writtenBy
|
||||
}
|
||||
|
||||
// resolveRoots returns the directories to walk for the given wing/hall
|
||||
// filters. Validates hall against the closed vocabulary when set.
|
||||
func resolveRoots(brainDir, wing, hall string) ([]string, error) {
|
||||
|
||||
Reference in New Issue
Block a user