Version, authenticate and fully trace ecosystem calls #84

Merged
claude merged 135 commits from overnight/eco-versioned-traces into master 2026-08-01 14:50:26 +02:00
2 changed files with 36 additions and 5 deletions
Showing only changes of commit df3220d039 - Show all commits
+22
View File
@@ -417,6 +417,28 @@ func TestRequirement_SwapModel(t *testing.T) {
}
}
// TestRequirement_ListMutation — the three methods that change what is on his
// lists sit on one rung. IngestMail joined them on 2026-08-01; it used to be
// AuthRead, which made it disagree with SetTaskStatus about the same question.
// CaptureTask stays a read: it puts one line on a list he asked for.
func TestRequirement_ListMutation(t *testing.T) {
for _, m := range []ipc.Method{
ipc.MethodIngestMail, ipc.MethodSetTaskStatus,
} {
if got := Requirement(m); got != AuthWrite {
t.Errorf("%s authority = %v; want AuthWrite", m, got)
}
}
if got := Requirement(ipc.MethodCaptureTask); got != AuthRead {
t.Errorf("CaptureTask authority = %v; want AuthRead", got)
}
// mavmaild keeps working: AuthWrite outside WriteFact only needs enrollment.
maild := Scope{Surface: SurfaceCoreProcess, Module: "mavmaild", SourceScope: []string{"mail:*"}}
if err := Can(ipc.MethodIngestMail, maild, nil); err != nil {
t.Errorf("mavmaild ingesting mail = %v; want allowed", err)
}
}
// TestRequirement_Capture — recording other people is a write, not a read: it
// puts audio of them on disk. The read side, "что ты записываешь?", is not.
//
+14 -5
View File
@@ -93,6 +93,20 @@ func Requirement(m ipc.Method) Authority {
return AuthWrite
case ipc.MethodWriteFact:
return AuthWrite
case ipc.MethodIngestMail:
// Mail ingestion (Vikunja #246). Moved up from AuthRead on 2026-08-01,
// for consistency with SetTaskStatus rather than for a new threat: both
// answer the same question — may this module change what is on his
// lists? — and they were answering it differently. The old argument was
// that ingestion is additive and can only produce candidate tasks, which
// is still true; it is the weaker half of the argument, because a
// compromised mail reader that can fill the review page indefinitely is
// not a read.
//
// No caller loses anything: AuthWrite outside WriteFact only requires
// enrollment, which mavmaild already has, and the method does not exist
// unless the operator wired a mail block.
return AuthWrite
case ipc.MethodSetTaskStatus:
// Resolving a task is NOT additive, which is what separates it from
// capture. Capture at AuthRead can only put a line on a list he reads
@@ -122,11 +136,6 @@ func Requirement(m ipc.Method) Authority {
// something.
ipc.MethodCaptureTask,
ipc.MethodListTasks,
// Mail ingestion (Vikunja #246). AuthRead because of what the method can
// produce: candidate tasks and nothing else. It cannot write a fact, set a
// reminder, or touch the tool allowlist, so a compromised mail reader can
// at worst put junk on a review page he clears in one click.
ipc.MethodIngestMail,
// Looking at one image (Vikunja #252). AuthRead because of what it can
// produce: words about a picture, and optionally a note. It cannot write
// a fact, set a reminder, or touch the tool allowlist. The invasive part