Add verified project audit links and button actors

Why: Owner-only command audit cannot prove shared business commits, and button dispatch omitted its authenticated actor.

Evidence: Local go test ./... passed; isolated PostgreSQL cutoff, link, and SQLite-to-PostgreSQL migration receipts are recorded as SELF in the task card.

Impact: Add an optional domain-verified metadata link, exact authorized cross-member read, idempotent storage, actor propagation, and honest PostgreSQL cutoff coverage. Broad audit pagination and other sources remain pending.

Refs: Stage 5 W9; approved Stage 6 audit-link design adjustment r1
This commit is contained in:
weichen
2026-10-14 04:05:10 +00:00
parent c8dd169a1f
commit 48fdddee25
18 changed files with 812 additions and 29 deletions
+3 -1
View File
@@ -36,7 +36,9 @@ import (
// ResourceAuthzQuery.Source for per-document publication authorization.
// 2.13.0-experimental: optional ProjectAuditHost and ProjectAuditRegistry (X2
// project-bound command-audit read slice and domain audit.read callback).
const SurfaceVersion = "2.13.0-experimental"
// 2.14.0-experimental: X2 optional committed project-audit link publisher,
// verifier, disclosure callback, and exact linked receipt read.
const SurfaceVersion = "2.14.0-experimental"
// ErrNotLinked 表示进程没有空白导入 link 包,Main 无法启动服务。
var ErrNotLinked = errors.New("extension: runner not linked; blank-import agent-system-backend/pkg/extension/link")
+75 -3
View File
@@ -20,18 +20,89 @@ type ProjectAuditRegistry interface {
}
var (
ErrProjectAuditDenied = errors.New("extension: project audit denied")
ErrProjectAuditUnsupported = errors.New("extension: project audit unsupported")
ErrProjectAuditDenied = errors.New("extension: project audit denied")
ErrProjectAuditUnsupported = errors.New("extension: project audit unsupported")
ErrProjectAuditLinkConflict = errors.New("extension: project audit link conflict")
)
// ProjectAuditLinkRequest binds one host command receipt to an already
// committed domain fact. Owner is never caller supplied; the host handle and
// stored audit row establish it. This first slice supports domain-commit only.
type ProjectAuditLinkRequest struct {
ProjectID core.ID `json:"projectId"`
HostAuditOffset int64 `json:"hostAuditOffset"`
SourceKind string `json:"sourceKind"`
SourceID string `json:"sourceId"`
OperationID string `json:"operationId"`
ResourceKind string `json:"resourceKind"`
ResourceID string `json:"resourceId"`
RunID string `json:"runId,omitempty"`
DisclosureClass string `json:"disclosureClass"`
IdempotencyKey string `json:"idempotencyKey"`
}
// ProjectAuditLinkProof contains the caller request plus identity and source
// fields re-read from trusted host storage. The domain verifier must check its
// committed source event and operation/resource attribution, not a model claim.
type ProjectAuditLinkProof struct {
ProjectAuditLinkRequest
Owner string
}
type ProjectAuditLinkVerifier func(context.Context, ProjectAuditLinkProof) (bool, error)
// ProjectAuditLinkedResource is the stored, immutable link presented to the
// current domain disclosure policy. A caller ID alone never authorizes it.
type ProjectAuditLinkedResource struct {
ProjectID core.ID
Owner string
SourceKind string
SourceID string
OperationID string
ResourceKind string
ResourceID string
RunID string
DisclosureClass string
}
type ProjectAuditDisclosure struct {
Metadata bool
Body bool
}
type ProjectAuditDisclosureAuthorizer func(context.Context, string, ProjectAuditLinkedResource) (ProjectAuditDisclosure, error)
// ProjectAuditLinkRegistry is optional. Only the registering compiled domain
// plugin may publish or read its links.
type ProjectAuditLinkRegistry interface {
RegisterProjectAuditLinkVerifier(ProjectAuditLinkVerifier) error
RegisterProjectAuditDisclosureAuthorizer(ProjectAuditDisclosureAuthorizer) error
}
type ProjectAuditLinkReceipt struct {
LinkID int64 `json:"linkId"`
HostAuditOffset int64 `json:"hostAuditOffset"`
}
type ProjectAuditPublisher interface {
PublishProjectAuditLink(context.Context, ProjectAuditLinkRequest) (ProjectAuditLinkReceipt, error)
}
// ProjectAuditQuery reads one project-bound command-audit slice. Offset is a
// host source offset, not a cross-domain event sequence. A nonzero BeforeOffset
// must be paired with the CutoffOffset returned on the first page.
// must be paired with the CutoffOffset returned on the first page. On PostgreSQL
// that cutoff bounds offsets but is not a fixed visibility snapshot: a lower ID
// from a still-open transaction may commit later. See Coverage.CutoffConsistency.
type ProjectAuditQuery struct {
ProjectID core.ID `json:"projectId"`
BeforeOffset int64 `json:"beforeOffset,omitempty"`
CutoffOffset int64 `json:"cutoffOffset,omitempty"`
Limit int `json:"limit,omitempty"`
// Linked* select one exact committed link. This avoids enumerating hidden
// private links or leaking their counts before cursor policy is complete.
LinkedSourceKind string `json:"linkedSourceKind,omitempty"`
LinkedSourceID string `json:"linkedSourceId,omitempty"`
LinkedHostOffset int64 `json:"linkedHostOffset,omitempty"`
}
// HostAuditReceipt is a server-projected receipt, never a raw audit payload.
@@ -52,6 +123,7 @@ type HostAuditReceipt struct {
// host sources and old unbound rows cannot be inferred from owner-wide audit.
type ProjectAuditCoverage struct {
CommandAudit string `json:"commandAudit"`
CutoffConsistency string `json:"cutoffConsistency"`
CrossMember string `json:"crossMember"`
LegacyProjectBinding string `json:"legacyProjectBinding"`
RunRelations string `json:"runRelations"`
+13
View File
@@ -9,6 +9,7 @@ AdminZoneSpec
Compiled
ErrNotLinked
ErrProjectAuditDenied
ErrProjectAuditLinkConflict
ErrProjectAuditUnsupported
Handler
Host
@@ -37,9 +38,21 @@ NewScopedHost
Options
ProjectAuditAuthorizer
ProjectAuditCoverage
ProjectAuditDisclosure
ProjectAuditDisclosureAuthorizer
ProjectAuditHost
ProjectAuditHost.QueryProjectAudit
ProjectAuditLinkProof
ProjectAuditLinkReceipt
ProjectAuditLinkRegistry
ProjectAuditLinkRegistry.RegisterProjectAuditDisclosureAuthorizer
ProjectAuditLinkRegistry.RegisterProjectAuditLinkVerifier
ProjectAuditLinkRequest
ProjectAuditLinkVerifier
ProjectAuditLinkedResource
ProjectAuditPage
ProjectAuditPublisher
ProjectAuditPublisher.PublishProjectAuditLink
ProjectAuditQuery
ProjectAuditRegistry
ProjectAuditRegistry.RegisterProjectAuditAuthorizer