Files
hyperguild/ingestion/internal/capture/entities.go
T
mathiasandClaude Opus 5.5 32dedb318d
CI / Lint / Test / Vet (pull_request) Successful in 43s
CI / Mirror to GitHub (pull_request) Skipped
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>
2026-10-01 20:49:11 +02:00

134 lines
5.1 KiB
Go

// Package capture is the Clean-Architecture use-case for the uniform
// capture capability (issue #49/#51): persist a finished session's
// valuable output — insights → brain, action items → Gitea tickets —
// with one invocation, identical core behaviour across every harness.
//
// This package is pure orchestration. It depends only on ports
// (interfaces) and plain entities — no HTTP, no live Gitea, no embedding
// or audit I/O. The real adapters are wired in #52 (Gitea tracker), #53
// (REST + I1 origin gate), and #54/#55 (audit path + relay). The I1
// sovereignty refusal and the classification-aware audit degradation are
// deliberately NOT here — those need the server-derived principal origin
// (#53) and the loki/buffer machinery (#54). What lives here is everything
// testable against fakes: validation, effective-classification resolution
// (stricter wins), best-effort orchestration, and the partial receipt.
package capture
// Zone is the trust zone a capture originates from, server-derived from
// the authenticated principal (spec §4.2 / I1). It is NEVER taken from
// caller input — context.Harness is descriptive telemetry only.
type Zone int
const (
// ZoneUnknown means the origin was not set. The REST adapter always
// sets a concrete zone; the service treats Unknown as "not gated" (only
// an explicit ZoneUSNexus triggers the I1 refusal) so the gate can
// never fire on a caller-controllable default.
ZoneUnknown Zone = iota
// ZoneSovereign is sovereign soil (homelab / Tailscale CLI callers).
ZoneSovereign
// ZoneUSNexus is a non-sovereign US-jurisdiction surface (e.g.
// claude.ai). Confidential captures through it are refused (I1).
ZoneUSNexus
)
// String renders the zone for audit/refusal messages.
func (z Zone) String() string {
switch z {
case ZoneSovereign:
return "sovereign-soil"
case ZoneUSNexus:
return "us-nexus"
default:
return "unknown"
}
}
// CaptureContext is the per-session metadata accompanying a capture.
//
// Classification is the caller-declared sensitivity (model C, spec §4.1):
// the server independently derives the target's classification and gates
// on the stricter of the two. Principal and Origin are server-derived from
// the authenticated identity (the REST adapter populates them); they are
// never caller-asserted. Harness is descriptive telemetry only — never a
// gate input.
type CaptureContext struct {
Harness string
SessionRef string
Fidelity string
Actor string
Classification string // caller-declared level token ("" = unspecified)
Principal string // server-derived (auth); audit identity
Origin Zone // server-derived trust zone; the I1 gate input
AuthPath string // server-derived: "static" or "jwt"; stamped as note provenance (#145)
}
// Insight is one piece of session knowledge bound for the brain. A
// non-empty SupersedeSlug routes to Update (revise in place); otherwise
// Write (create).
type Insight struct {
Text string
Wing string
Hall string
SupersedeSlug string
}
// Ticket is one action item bound for a Gitea repo. Owner is always the
// operator (set by the tracker adapter), never carried here.
type Ticket struct {
Repo string
Action string // create | close | comment
Number int // required for close/comment
Title string // required for create
Body string
}
// CaptureInput is the whole capture request.
type CaptureInput struct {
Context CaptureContext
Insights []Insight
Tickets []Ticket
DryRun bool
}
// InsightResult is the per-insight outcome in the receipt.
type InsightResult struct {
ID string `json:"id,omitempty"`
Path string `json:"path,omitempty"`
ContentHash string `json:"content_hash,omitempty"`
Superseded bool `json:"superseded"`
OK bool `json:"ok"`
}
// TicketResult is the per-ticket outcome in the receipt.
type TicketResult struct {
Repo string `json:"repo"`
Number int `json:"number,omitempty"`
Action string `json:"action"`
URL string `json:"url,omitempty"`
OK bool `json:"ok"`
}
// ItemError pins a failure to a specific request item for the partial
// receipt. Item is a stable locator like "insight[1]" or "ticket[0]".
type ItemError struct {
Item string `json:"item"`
Error string `json:"error"`
}
// CaptureReceipt is the structured, partial-aware result. Per-item ok
// flags plus a flat Errors list make partial success explicit; the
// caller never has to infer what landed.
type CaptureReceipt struct {
Insights []InsightResult `json:"insights"`
Tickets []TicketResult `json:"tickets"`
Errors []ItemError `json:"errors"`
EffectiveClassification string `json:"effective_classification,omitempty"`
DryRun bool `json:"dry_run"`
// AuditBuffered is true when the central audit sink was unreachable and
// this capture's audit record was written to the durable local buffer
// instead (internal/public tier). Surfaces the degraded state to the
// caller per §4.4.
AuditBuffered bool `json:"audit_buffered,omitempty"`
}