211 lines
9.8 KiB
Go
211 lines
9.8 KiB
Go
package extension
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
core "agent-system-core"
|
|
)
|
|
|
|
// ProjectAuditAuthorizer answers the domain's audit.read decision for the
|
|
// authenticated actor and a project identified by trusted host storage.
|
|
// A project ID supplied by a caller is a locator, never an authorization.
|
|
type ProjectAuditAuthorizer func(actor string, projectID core.ID) (bool, error)
|
|
|
|
// ProjectAuditRegistry is optional so older extension registries remain valid.
|
|
// Only an in-process domain plugin can register this callback.
|
|
type ProjectAuditRegistry interface {
|
|
RegisterProjectAuditAuthorizer(ProjectAuditAuthorizer) error
|
|
}
|
|
|
|
var (
|
|
ErrProjectAuditDenied = errors.New("extension: project audit denied")
|
|
ErrProjectAuditUnsupported = errors.New("extension: project audit unsupported")
|
|
ErrProjectAuditLinkConflict = errors.New("extension: project audit link conflict")
|
|
ErrProjectAuditPending = errors.New("extension: committed host audit receipt pending")
|
|
)
|
|
|
|
// 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. HostAuditOffset=0 asks the host to resolve a
|
|
// unique committed row by its trusted owner/project/OperationID, which lets an
|
|
// outbox wait for the bus audit commit after the domain handler returns. 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
|
|
}
|
|
|
|
// ProjectAuditDisclosureAuthorizer answers one per-object decision for the
|
|
// authenticated actor on a stored, immutable link. Metadata and Body are
|
|
// separate authorities: the host never substitutes one for the other, and it
|
|
// never answers a body request from a metadata-only right.
|
|
//
|
|
// TODO(RDM consumer, out of this repository): the decision itself — the current
|
|
// project audit.read grant, the document read duty plus stage scope that carries
|
|
// 正文权, and any export authority — belongs to the domain plugin that registers
|
|
// this callback. The Backend only refuses, trims, and reports coverage; it must
|
|
// not grow a local account/project policy to answer in the domain's place. A
|
|
// later RDM unit implements the callbacks and can then widen Coverage
|
|
// (Export, ResourceBodies, RunRelations) from this base's current honest
|
|
// "unsupported" statements.
|
|
type ProjectAuditDisclosureAuthorizer func(context.Context, string, ProjectAuditLinkedResource) (ProjectAuditDisclosure, error)
|
|
|
|
// ProjectAuditPublisherFactory is issued only by successful verifier
|
|
// registration. A domain outbox may retain it and bind an owner for each job;
|
|
// owner and committed-source attribution are checked again when publishing.
|
|
// NewScopedHost(owner, pluginID) cannot mint this capability.
|
|
type ProjectAuditPublisherFactory interface {
|
|
ForOwner(owner string) (ProjectAuditPublisher, error)
|
|
}
|
|
|
|
// ProjectAuditLinkRegistry is optional. Only the registering compiled domain
|
|
// plugin receives a publisher factory; string plugin IDs carry no authority.
|
|
type ProjectAuditLinkRegistry interface {
|
|
RegisterProjectAuditLinkVerifier(ProjectAuditLinkVerifier) (ProjectAuditPublisherFactory, 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. PostgreSQL
|
|
// pages are served only while the installed generator, sequence, and all-writer
|
|
// database guards prove a
|
|
// committed prefix; otherwise the host rejects the read instead of claiming a
|
|
// fixed snapshot. See Coverage.CutoffConsistency.
|
|
//
|
|
// A read is keyed by the trusted project, the authenticated actor, the verified
|
|
// resource and run, and the source offset plus cutoff. Every caller-supplied
|
|
// field is a locator, never an authorization: the host re-checks it against
|
|
// stored host facts and the domain callbacks before a receipt is projected.
|
|
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"`
|
|
// LinkedResourceKind/LinkedResourceID/LinkedRunID narrow the same exact
|
|
// locator to the verified binding. A value that disagrees with stored host
|
|
// state denies the read; it never widens it and never stands in for the
|
|
// domain's current decision on that object.
|
|
LinkedResourceKind string `json:"linkedResourceKind,omitempty"`
|
|
LinkedResourceID string `json:"linkedResourceId,omitempty"`
|
|
LinkedRunID string `json:"linkedRunId,omitempty"`
|
|
// IncludeBody asks for the per-object body projection of one verified link.
|
|
// The host asks the domain disclosure authorizer again for that object: a
|
|
// metadata-only right serves metadata only, and a request without a verified
|
|
// object binding is reported unsupported instead of a silently trimmed page.
|
|
IncludeBody bool `json:"includeBody,omitempty"`
|
|
// Export asks for a whole-history export. No base capability serves one, so
|
|
// the host refuses instead of degrading into a partial paginated read that
|
|
// would claim a coverage it cannot establish. See Coverage.Export.
|
|
Export bool `json:"export,omitempty"`
|
|
}
|
|
|
|
// ProjectAuditReceiptResource names the object one verified link attributes a
|
|
// receipt to. The host copies it from the stored binding and only when the
|
|
// domain granted the per-object body right; it is never derived from caller
|
|
// input and never carries resource content.
|
|
type ProjectAuditReceiptResource struct {
|
|
Kind string `json:"kind"`
|
|
ID string `json:"id"`
|
|
RunID string `json:"runId,omitempty"`
|
|
}
|
|
|
|
// HostAuditReceipt is a server-projected receipt, never a raw audit payload.
|
|
// ActorName is the name recorded with this command; old rows have no snapshot.
|
|
// Resource appears only for a body-permitted verified link.
|
|
type HostAuditReceipt struct {
|
|
SourceOffset int64 `json:"sourceOffset"`
|
|
Time time.Time `json:"time"`
|
|
ActorID string `json:"actorId,omitempty"`
|
|
ActorName string `json:"actorName,omitempty"`
|
|
ActorNameStatus string `json:"actorNameStatus"`
|
|
CommandID string `json:"commandId"`
|
|
State string `json:"state"`
|
|
OperationID string `json:"operationId,omitempty"`
|
|
ConversationID string `json:"conversationId"`
|
|
Resource *ProjectAuditReceiptResource `json:"resource,omitempty"`
|
|
}
|
|
|
|
// ProjectAuditCoverage states the exact scope of this first X2 slice. Missing
|
|
// 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"`
|
|
ModelReceipts string `json:"modelReceipts"`
|
|
BackgroundReceipts string `json:"backgroundReceipts"`
|
|
ResourceBodies string `json:"resourceBodies"`
|
|
// Export states whether a whole-history export is served. An audit grant is
|
|
// not an export grant: this slice only pages, and says so.
|
|
Export string `json:"export"`
|
|
}
|
|
|
|
type ProjectAuditPage struct {
|
|
Receipts []HostAuditReceipt `json:"receipts"`
|
|
NextOffset int64 `json:"nextOffset,omitempty"`
|
|
CutoffOffset int64 `json:"cutoffOffset"`
|
|
Coverage ProjectAuditCoverage `json:"coverage"`
|
|
}
|
|
|
|
// ProjectAuditHost is an optional, actor-bound read capability. A consumer must
|
|
// treat a failed type assertion as unsupported; an authorized page still reports
|
|
// partial coverage explicitly.
|
|
type ProjectAuditHost interface {
|
|
QueryProjectAudit(context.Context, ProjectAuditQuery) (ProjectAuditPage, error)
|
|
}
|