feat(capture): I1 sovereignty gate + server-derived origin (#53)
Adds the trust-zone Origin to CaptureContext and the I1 gate to the use-case: a confidential effective classification through a us-nexus origin is refused before ANY write (ErrSovereigntyRefused), and the refusal is itself audited. A caller-asserted harness label that names a different zone than the server-derived origin is logged as a security event — context.Harness is descriptive-only, never a gate input. The gate triggers only on an explicit ZoneUSNexus, so the unset default (ZoneUnknown) can never make it fire on caller-controllable input; the REST adapter always sets a concrete zone. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,33 @@ func NewService(b BrainStore, tr IssueTracker, sw SummaryWriter, p Classificatio
|
||||
|
||||
var validActions = map[string]bool{"create": true, "close": true, "comment": true}
|
||||
|
||||
// ErrSovereigntyRefused is returned when the I1 gate refuses a capture
|
||||
// (confidential effective classification through a us-nexus origin). The
|
||||
// REST adapter maps it to HTTP 403. Callers test with errors.Is.
|
||||
var ErrSovereigntyRefused = fmt.Errorf("capture refused by I1 sovereignty gate")
|
||||
|
||||
// assertedZoneMismatch returns a security-event string when the caller's
|
||||
// harness label asserts a trust zone that contradicts the server-derived
|
||||
// origin. A harness label that names no zone (the normal case, e.g.
|
||||
// "claude-code") returns "". The label is never used as a gate input —
|
||||
// this only flags the discrepancy for the audit trail.
|
||||
func assertedZoneMismatch(harness string, derived Zone) string {
|
||||
var asserted Zone
|
||||
switch strings.ToLower(strings.TrimSpace(harness)) {
|
||||
case "sovereign-soil", "sovereign":
|
||||
asserted = ZoneSovereign
|
||||
case "us-nexus", "usnexus":
|
||||
asserted = ZoneUSNexus
|
||||
default:
|
||||
return "" // no zone claim
|
||||
}
|
||||
if asserted != derived {
|
||||
return fmt.Sprintf("asserted-vs-derived origin mismatch: harness asserted %s, principal resolves to %s",
|
||||
asserted, derived)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Capture runs the use-case: validate (fail-closed), resolve effective
|
||||
// classification (stricter of declared vs target-derived), then persist
|
||||
// insights → tickets → summary best-effort, emit an audit record, and
|
||||
@@ -57,6 +84,32 @@ func (s *Service) Capture(ctx context.Context, in CaptureInput) (CaptureReceipt,
|
||||
|
||||
effective, securityEvents := s.resolveClassification(declared, in)
|
||||
|
||||
// Server-derived origin governs the I1 gate; a caller-asserted harness
|
||||
// label that names a different zone is descriptive-only and logged as a
|
||||
// security event (spec §4.2: a control keyed on attacker-suppliable
|
||||
// input is not a control).
|
||||
if ev := assertedZoneMismatch(in.Context.Harness, in.Context.Origin); ev != "" {
|
||||
securityEvents = append(securityEvents, ev)
|
||||
}
|
||||
|
||||
// I1 sovereignty gate: a confidential capture through a us-nexus origin
|
||||
// is refused before ANY write. The refusal itself is audited (best
|
||||
// effort) — refusals must be reconstructable too.
|
||||
if effective == classification.Confidential && in.Context.Origin == ZoneUSNexus {
|
||||
_ = s.audit.Record(ctx, AuditEntry{
|
||||
Timestamp: s.now().UTC(),
|
||||
Principal: in.Context.Principal,
|
||||
Actor: in.Context.Actor,
|
||||
Harness: in.Context.Harness,
|
||||
SessionRef: in.Context.SessionRef,
|
||||
EffectiveClassification: effective.String(),
|
||||
Items: nil, // refused before any write
|
||||
SecurityEvents: append(securityEvents, "I1 refusal: confidential capture via us-nexus origin"),
|
||||
})
|
||||
return CaptureReceipt{}, fmt.Errorf("%w: effective classification confidential through %s origin",
|
||||
ErrSovereigntyRefused, in.Context.Origin)
|
||||
}
|
||||
|
||||
receipt := CaptureReceipt{
|
||||
Errors: []ItemError{},
|
||||
EffectiveClassification: effective.String(),
|
||||
|
||||
Reference in New Issue
Block a user