delegation

package
v0.3.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 8, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package delegation defines the canonical durable contracts for Secure Task Delegation (B32): logical task delegation, messages/parts, terminal results, transferable artifact references (schema only), ordered task events, idempotency, content digests, and a pluggable Store interface.

T01 delivers the schemas, validation, transitions, and store interface. T02 adds snapshot-based two-sided authorization. T03 wires the SDK and gateway. T04 implements the artifact broker. T05 delivers event-driven wait/wake. T06 is the adversary gate.

Index

Constants

View Source
const (
	PrefixTask    = "task-"
	PrefixMessage = "msg-"
	PrefixResult  = "tres-"
	PrefixEvent   = "tevt-"
)

ID prefixes.

View Source
const (
	DenyCallerBinding      = "DENY_CALLER_BINDING"
	DenyCalleePolicy       = "DENY_CALLEE_POLICY"
	DenyUnpromoted         = "DENY_UNPROMOTED"
	DenySnapshotMismatch   = "DENY_SNAPSHOT_MISMATCH"
	DenyExpired            = "DENY_EXPIRED"
	DenyBudget             = "DENY_BUDGET"
	ErrIdempotencyConflict = "ERR_IDEMPOTENCY_CONFLICT"
	ErrSequenceGap         = "ERR_SEQUENCE_GAP"
	ErrInvalidMessage      = "ERR_INVALID_MESSAGE"
	ErrForbiddenContent    = "ERR_FORBIDDEN_CONTENT"
)
View Source
const CapabilityHeader = "X-AgentPaaS-Capability"

CapabilityHeader is the internal HTTP/gRPC header name used to carry the per-binding capability token between the caller gateway and callee gateway. This header is STRIPPED before it reaches agent code and MUST NEVER appear in agent-facing responses, SDK DTOs, logs, or audit event payloads that are exposed beyond the gateway.

View Source
const CurrentSchemaVersion = "0.3.0"

CurrentSchemaVersion is the current schema version for delegation records.

Variables

View Source
var (
	ErrArtifactNotFound         = errors.New("delegation: artifact not found")
	ErrArtifactAudienceDenied   = errors.New("delegation: consumer not in audience")
	ErrArtifactExpired          = errors.New("delegation: artifact expired")
	ErrArtifactDigestMismatch   = errors.New("delegation: digest mismatch")
	ErrArtifactSizeExceeded     = errors.New("delegation: artifact exceeds max bytes")
	ErrArtifactInvalidPath      = errors.New("delegation: invalid artifact path")
	ErrArtifactSymlink          = errors.New("delegation: symlink rejected")
	ErrArtifactHardlink         = errors.New("delegation: hard link rejected")
	ErrArtifactWorkflowMismatch = errors.New("delegation: workflow mismatch")
)
View Source
var TaskTransitions = map[TaskStatus]map[TaskStatus]bool{
	TaskStatusPending: {
		TaskStatusAdmitted:  true,
		TaskStatusDenied:    true,
		TaskStatusCancelled: true,
		TaskStatusExpired:   true,
	},
	TaskStatusAdmitted: {
		TaskStatusRunning:   true,
		TaskStatusCancelled: true,
		TaskStatusExpired:   true,
		TaskStatusDenied:    true,
	},
	TaskStatusRunning: {
		TaskStatusSucceeded: true,
		TaskStatusFailed:    true,
		TaskStatusCancelled: true,
		TaskStatusExpired:   true,
	},

	TaskStatusSucceeded: {},
	TaskStatusFailed:    {},
	TaskStatusCancelled: {},
	TaskStatusExpired:   {},
	TaskStatusDenied:    {},
}

TaskTransitions defines legal TaskStatus transitions.

Functions

func AllErrorCodes

func AllErrorCodes() []string

AllErrorCodes returns all known stable error/denial codes.

func CanonicalJSON

func CanonicalJSON(v interface{}) ([]byte, error)

CanonicalJSON returns the canonical JSON bytes for any value (sorted keys, no trailing whitespace). Used for deterministic digests.

func CanonicalMessagePartsDigest

func CanonicalMessagePartsDigest(parts []MessagePart) (string, error)

CanonicalMessagePartsDigest returns the SHA-256 hex digest of the canonical JSON representation of the message parts. Parts are sorted by kind, then text, for deterministic output.

func CanonicalResultDigest

func CanonicalResultDigest(r *Result) (string, error)

CanonicalResultDigest returns the SHA-256 hex digest of the canonical JSON representation of a Result struct (excluding the digest field itself).

func ComputeSnapshotDigest

func ComputeSnapshotDigest(s *CommunicationSnapshot) (string, error)

ComputeSnapshotDigest produces a deterministic SHA-256 hex digest over the canonical JSON of the snapshot (excluding the SnapshotDigest field itself). Bindings are sorted by BindingID for deterministic output.

func DeriveCapabilityTokenForTest

func DeriveCapabilityTokenForTest(expect BindingExpectation) string

DeriveCapabilityTokenForTest derives a deterministic capability token from a BindingExpectation. This is for tests only — production MUST use randomly generated tokens from GenerateCapabilityToken stored in BindingCapabilities.

func GenerateCapabilityToken

func GenerateCapabilityToken() (string, error)

GenerateCapabilityToken produces a cryptographically random unguessable capability token. This is called by the trusted harness/gateway at invoke bootstrap; the resulting token is stored in BindingCapabilities and NEVER serialized into agent responses.

func Sha256Hex

func Sha256Hex(data []byte) string

Sha256Hex returns the SHA-256 hex digest of arbitrary bytes.

func ValidErrorCode

func ValidErrorCode(code string) bool

ValidErrorCode returns true if code is a known stable error/denial code.

func ValidateIDPrefix

func ValidateIDPrefix(id, prefix string) bool

ValidateIDPrefix returns true if id is non-empty and starts with prefix.

func ValidateMessage

func ValidateMessage(m *Message) error

ValidateMessage validates a Message envelope. Rejects control chars, secret sentinels, oversized parts, forbidden content, and endpoint-like fields.

func ValidateResult

func ValidateResult(r *Result) error

ValidateResult validates a task Result.

func ValidateSystemRole

func ValidateSystemRole(role MessageRole, writer WriterKind) error

ValidateSystemRole returns an error if the role is system and the writer is not a trusted runtime.

func ValidateTask

func ValidateTask(t *Task) error

ValidateTask validates a Task record.

func ValidateTaskEvent

func ValidateTaskEvent(ev *TaskEvent) error

ValidateTaskEvent performs lightweight validation on a TaskEvent.

func ValidateTaskTransition

func ValidateTaskTransition(from, to TaskStatus) error

ValidateTaskTransition validates that a TaskStatus transition is legal. Returns nil if valid, or a *TransitionError if invalid.

func ValidateTaskTransitionString

func ValidateTaskTransitionString(from, to string) error

ValidateTaskTransitionString validates a task transition by string names. This is a convenience for tests and API layers.

func ValidateTransferableArtifactRef

func ValidateTransferableArtifactRef(ref *TransferableArtifactRef) error

ValidateTransferableArtifactRef validates a TransferableArtifactRef.

Types

type ArtifactBroker

type ArtifactBroker interface {
	// Commit writes the artifact blob, records metadata, and returns an
	// immutable TransferableArtifactRef.
	Commit(ctx context.Context, req CommitReq) (TransferableArtifactRef, error)

	// AuthorizeRead checks that consumerLogicalID is in the artifact's
	// audience, the workflow matches, and the ref has not expired.
	AuthorizeRead(ctx context.Context, artifactID, consumerLogicalID, workflowID string, now time.Time) error

	// ProjectReadOnly verifies digest, audience, expiry, and returns a
	// ProjectedArtifact with a read-only host path under the broker store.
	ProjectReadOnly(ctx context.Context, artifactID, consumerLogicalID string, now time.Time) (ProjectedArtifact, error)

	// VerifyDigest re-hashes the blob and compares against the recorded digest.
	VerifyDigest(ctx context.Context, artifactID string) error
}

ArtifactBroker manages digest-bound, audience-scoped artifact transfer. Committed artifacts are immutable; consumers receive read-only projected paths after authorization.

type AuthorizeRequest

type AuthorizeRequest struct {
	Snapshot *CommunicationSnapshot

	BindingID string
	Operation string

	// Caller's live identity (must match snapshot caller pin).
	CallerDeploymentID  string
	CallerPackageDigest string

	// Requested callee (must match binding pin).
	CalleePackageName    string
	CalleePackageVersion string
	CalleeBundleDigest   string

	DataClass string

	// CalleeIngressAllow is the callee's ingress policy from its package/deployment.
	CalleeIngressAllow []CalleeIngressRule

	// PromotedLookup is an optional hook: if set and returns false, DENY_UNPROMOTED.
	PromotedLookup func(packageName, version, digest string) (bool, error)

	// ExpectedSnapshotGeneration is the snapshot generation the caller
	// asserts. When non-zero, AuthorizeDelegation rejects with
	// DENY_SNAPSHOT_MISMATCH if it doesn't match the snapshot. Zero
	// value means "not enforced" (backward compat for tests that don't
	// set it).
	ExpectedSnapshotGeneration int64

	Now time.Time
}

AuthorizeRequest carries all information needed for two-sided authorization.

type AuthzAuditRecord

type AuthzAuditRecord struct {
	TaskID             string       `json:"task_id"`
	WorkflowID         string       `json:"workflow_id"`
	SnapshotGeneration int64        `json:"snapshot_generation"`
	SnapshotDigest     string       `json:"snapshot_digest"`
	BindingID          string       `json:"binding_id"`
	CallerDecision     SideDecision `json:"caller_decision"`
	CalleeDecision     SideDecision `json:"callee_decision"`
	DenialCode         string       `json:"denial_code"`
	DecidedAt          time.Time    `json:"decided_at"`
}

AuthzAuditRecord is a pure-data record of an authorization decision. Suitable for writing to the audit log.

func NewAuthzAuditRecord

func NewAuthzAuditRecord(
	taskID, workflowID string,
	snapshotGeneration int64,
	snapshotDigest string,
	bindingID string,
	decision AuthzDecision,
) AuthzAuditRecord

NewAuthzAuditRecord creates an audit record from an authorization decision.

type AuthzDecision

type AuthzDecision struct {
	Allowed        bool                       `json:"allowed"`
	CallerDecision SideDecision               `json:"caller_decision"`
	CalleeDecision SideDecision               `json:"callee_decision"`
	DenialCode     string                     `json:"denial_code,omitempty"`
	Binding        *WorkflowDelegationBinding `json:"binding,omitempty"`
}

AuthzDecision is the result of a two-sided authorization evaluation.

func AuthorizeDelegation

func AuthorizeDelegation(req *AuthorizeRequest) AuthzDecision

AuthorizeDelegation performs two-sided authorization: 1. Caller side: snapshot must name the binding + match caller/callee pins. 2. Callee side: ingress policy must allow the caller + binding. Both sides are always evaluated when possible. DenialCode prefers caller code if caller failed, else callee code.

func AuthorizeDelegationWithPromotion

func AuthorizeDelegationWithPromotion(
	req *AuthorizeRequest,
	lookup func(packageName, version, digest string) (bool, error),
) AuthzDecision

AuthorizeDelegationWithPromotion is a convenience wrapper that binds a PromotedLookup to AuthorizeRequest and calls AuthorizeDelegation.

type BindingExpectation

type BindingExpectation struct {
	BindingID   string
	WorkflowID  string
	CallerLease string
	CalleeLease string
}

BindingExpectation is the set of claims that the callee gateway validates against the capability token.

type CalleeIngressRule

type CalleeIngressRule struct {
	CallerPackageName   string   `json:"caller_package_name"`
	CallerPackageDigest string   `json:"caller_package_digest,omitempty"`
	AllowedBindings     []string `json:"allowed_bindings"`
	MaxDataClass        string   `json:"max_data_class"`
}

CalleeIngressRule defines who may call a callee.

type CalleeRef

type CalleeRef struct {
	DeploymentID   string `json:"deployment_id,omitempty"` // resolved at admission; empty for unresolved logical pin
	PackageName    string `json:"package_name"`
	PackageVersion string `json:"package_version"`
	PackageDigest  string `json:"package_digest"`
}

CalleeRef identifies the callee of a task.

type CallerRef

type CallerRef struct {
	DeploymentID  string `json:"deployment_id"`
	RunID         string `json:"run_id"`
	AttemptID     string `json:"attempt_id"`
	PackageName   string `json:"package_name"`
	PackageDigest string `json:"package_digest"`
}

CallerRef identifies the caller of a task.

type Classification

type Classification string

Classification represents the data sensitivity level.

const (
	ClassificationPublic       Classification = "public"
	ClassificationInternal     Classification = "internal"
	ClassificationConfidential Classification = "confidential"
	ClassificationRestricted   Classification = "restricted"
)

func AllClassifications

func AllClassifications() []Classification

AllClassifications returns all valid Classification values in order.

func (Classification) Valid

func (c Classification) Valid() bool

Valid returns true if c is a known Classification.

type CommitReq

type CommitReq struct {
	WorkflowID        string
	ProducerRunID     string
	ProducerAttemptID string
	ProducerTaskID    string
	LogicalRef        string
	MediaType         string
	Classification    Classification
	Audience          []string
	ExpiresAt         time.Time
	// Reader is the source data. May be nil (error). Read up to MaxBytes.
	Reader   io.Reader
	MaxBytes int64
}

CommitReq describes an artifact to commit.

type CommunicationSnapshot

type CommunicationSnapshot struct {
	SchemaVersion       string                      `json:"schema_version"`
	SnapshotGeneration  int64                       `json:"snapshot_generation"`
	WorkflowID          string                      `json:"workflow_id"`
	TenantID            string                      `json:"tenant_id"`
	CallerDeploymentID  string                      `json:"caller_deployment_id"`
	CallerPackageName   string                      `json:"caller_package_name"`
	CallerPackageDigest string                      `json:"caller_package_digest"`
	Bindings            []WorkflowDelegationBinding `json:"bindings"`
	SnapshotDigest      string                      `json:"snapshot_digest,omitempty"`
}

CommunicationSnapshot is the immutable, signed pin used at task admission. It binds a caller identity to a set of delegation bindings. The SnapshotDigest covers the caller identity, workflow, tenant, generation, and all bindings via canonical JSON.

type EventID

type EventID string

EventID identifies a task event.

func NewEventID

func NewEventID() (EventID, error)

NewEventID generates a cryptographically random event ID.

func (EventID) String

func (id EventID) String() string

String returns the string representation.

func (EventID) Validate

func (id EventID) Validate() bool

Validate returns true if the ID is non-empty and has the correct prefix.

type EventType

type EventType string

EventType represents the type of a task event.

const (
	EventTaskAdmitted      EventType = "TASK_ADMITTED"
	EventTaskDenied        EventType = "TASK_DENIED"
	EventTaskStarted       EventType = "TASK_STARTED"
	EventTaskMessage       EventType = "TASK_MESSAGE"
	EventTaskProgress      EventType = "TASK_PROGRESS"
	EventTaskSucceeded     EventType = "TASK_SUCCEEDED"
	EventTaskFailed        EventType = "TASK_FAILED"
	EventTaskCancelled     EventType = "TASK_CANCELLED"
	EventTaskExpired       EventType = "TASK_EXPIRED"
	EventArtifactCommitted EventType = "ARTIFACT_COMMITTED"
)

func AllEventTypes

func AllEventTypes() []EventType

AllEventTypes returns all valid EventType values.

func (EventType) MarshalJSON

func (t EventType) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler.

func (*EventType) UnmarshalJSON

func (t *EventType) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler.

func (EventType) Valid

func (t EventType) Valid() bool

Valid returns true if t is a known EventType.

type FileArtifactBroker

type FileArtifactBroker struct {
	// contains filtered or unexported fields
}

FileArtifactBroker stores artifacts on a local filesystem under a root.

func NewFileArtifactBroker

func NewFileArtifactBroker(rootPath string) (*FileArtifactBroker, error)

NewFileArtifactBroker creates a broker rooted at rootPath.

func (*FileArtifactBroker) AuthorizeRead

func (b *FileArtifactBroker) AuthorizeRead(ctx context.Context, artifactID, consumerLogicalID, workflowID string, now time.Time) error

AuthorizeRead checks authorization.

func (*FileArtifactBroker) Commit

Commit writes the artifact and returns its ref.

func (*FileArtifactBroker) ProjectReadOnly

func (b *FileArtifactBroker) ProjectReadOnly(ctx context.Context, artifactID, consumerLogicalID string, now time.Time) (ProjectedArtifact, error)

ProjectReadOnly returns a read-only artifact projection.

func (*FileArtifactBroker) VerifyDigest

func (b *FileArtifactBroker) VerifyDigest(ctx context.Context, artifactID string) error

VerifyDigest re-hashes the blob and compares to the stored digest.

type GatewayEnforcer

type GatewayEnforcer struct{}

GatewayEnforcer is the trusted component that attaches, validates, and strips per-binding capability tokens. Agent code never sees the token or the network alias.

In production, the full implementation maps capability tokens to binding state inside the gateway mesh. For T03, this is a thin stub that records enforcement points — every test path through ValidateAndStrip proves that the enforcement step was called and that token material is stripped.

func (*GatewayEnforcer) Attach

func (g *GatewayEnforcer) Attach(token string) map[string]string

Attach returns headers for the trusted caller-gateway path. The returned map contains the CapabilityHeader key with the token as its value. This MUST only be called by the trusted caller gateway — never by agent code.

func (*GatewayEnforcer) ValidateAndStrip

func (g *GatewayEnforcer) ValidateAndStrip(headers map[string]string, expectedToken string) error

ValidateAndStrip checks that headers contain a valid capability matching the expectedToken, then removes the CapabilityHeader from headers in place.

In production, expectedToken is the random token from BindingCapabilities. In tests, callers may use DeriveCapabilityTokenForTest to produce a deterministic token.

Returns an error if:

  • CapabilityHeader is missing
  • The token does not match the expectedToken

The error message MUST NOT contain the actual token value.

type MemoryStore

type MemoryStore struct {
	// contains filtered or unexported fields
}

MemoryStore is an in-memory implementation of Store for unit tests.

func NewMemoryStore

func NewMemoryStore(opts ...MemoryStoreOption) *MemoryStore

NewMemoryStore constructs an empty in-memory store.

func (*MemoryStore) AppendEvent

func (s *MemoryStore) AppendEvent(ctx context.Context, ev TaskEvent) (int64, error)

AppendEvent appends an event to a task's event stream.

func (*MemoryStore) AppendMessage

func (s *MemoryStore) AppendMessage(ctx context.Context, msg Message) error

AppendMessage appends a message to a task, enforcing contiguous sequence.

func (*MemoryStore) CASTask

func (s *MemoryStore) CASTask(ctx context.Context, t Task, expectedGen int64) error

CASTask updates a task atomically using compare-and-swap on generation.

func (*MemoryStore) CreateTask

func (s *MemoryStore) CreateTask(ctx context.Context, t Task) error

CreateTask persists a new task. Idempotent on caller_identity+idempotency_key.

func (*MemoryStore) GetResult

func (s *MemoryStore) GetResult(ctx context.Context, taskID TaskID) (*Result, error)

GetResult returns the result for a task.

func (*MemoryStore) GetTask

func (s *MemoryStore) GetTask(ctx context.Context, taskID TaskID) (*Task, error)

GetTask returns the task by ID.

func (*MemoryStore) GetTaskByIdempotencyKey

func (s *MemoryStore) GetTaskByIdempotencyKey(ctx context.Context, callerIdentity, idempotencyKeyStr string) (*Task, error)

GetTaskByIdempotencyKey returns the task matching callerIdentity and idempotencyKey, or nil if not found.

func (*MemoryStore) ListEvents

func (s *MemoryStore) ListEvents(ctx context.Context, taskID TaskID, afterSeq int64) ([]TaskEvent, error)

ListEvents returns events for a task with sequence > afterSeq.

func (*MemoryStore) ListMessages

func (s *MemoryStore) ListMessages(ctx context.Context, taskID TaskID, afterSeq int64) ([]Message, error)

ListMessages returns messages for a task with sequence > afterSeq.

func (*MemoryStore) PutResult

func (s *MemoryStore) PutResult(ctx context.Context, r Result) error

PutResult stores the terminal result for a task.

func (*MemoryStore) SubscribeEvents

func (s *MemoryStore) SubscribeEvents(ctx context.Context, taskID TaskID, afterSeq int64) (<-chan TaskEvent, func(), error)

SubscribeEvents returns a channel of events for a task, replaying existing events with sequence > afterSeq. The channel is closed when the context is cancelled or the task's event stream reaches a terminal event.

type MemoryStoreOption

type MemoryStoreOption func(*MemoryStore)

MemoryStoreOption configures a MemoryStore.

func WithMemoryClock

func WithMemoryClock(now func() time.Time) MemoryStoreOption

WithMemoryClock injects a fake clock.

type Message

type Message struct {
	SchemaVersion string    `json:"schema_version"`
	MessageID     MessageID `json:"message_id"`
	TaskID        TaskID    `json:"task_id"`
	WorkflowID    string    `json:"workflow_id"`
	TenantID      string    `json:"tenant_id"`

	// Sequence is monotonic per task, starting at 1.
	Sequence int64 `json:"sequence"`

	Role               MessageRole `json:"role"`
	SenderLogicalID    string      `json:"sender_logical_id"`
	RecipientLogicalID string      `json:"recipient_logical_id"`

	Parts []MessagePart `json:"parts"`

	// ContentDigest is the SHA-256 hex of the canonical parts JSON.
	ContentDigest string `json:"content_digest"`

	// ByteSize is the total message byte size (≤ 256 KiB).
	ByteSize int64 `json:"byte_size"`

	Classification Classification `json:"classification"`

	CreatedAt time.Time `json:"created_at"`

	// IdempotencyKey is optional.
	IdempotencyKey string `json:"idempotency_key,omitempty"`
}

Message is a durable message envelope within a task. Forbidden: endpoints, IPs, DNS, ports, capability tokens, raw URLs, secret sentinels, control characters, hidden_reasoning, provider continuation identifiers.

type MessageID

type MessageID string

MessageID identifies a message.

func NewMessageID

func NewMessageID() (MessageID, error)

NewMessageID generates a cryptographically random message ID.

func (MessageID) String

func (id MessageID) String() string

String returns the string representation.

func (MessageID) Validate

func (id MessageID) Validate() bool

Validate returns true if the ID is non-empty and has the correct prefix.

type MessagePart

type MessagePart struct {
	Kind PartKind `json:"kind"`

	// Text is bounded (max 64 KiB UTF-8). Set for kind=text, kind=error.
	Text string `json:"text,omitempty"`

	// JSON is a raw JSON message (size-bounded). Set for kind=json.
	JSON string `json:"json,omitempty"`

	// ArtifactRef is a logical artifact reference. Set for kind=artifact_ref.
	ArtifactRef string `json:"artifact_ref,omitempty"`

	// MediaType is the MIME type for this part.
	MediaType string `json:"media_type,omitempty"`
}

MessagePart is a single typed part within a message.

type MessageRole

type MessageRole string

MessageRole represents the role of a message sender.

const (
	RoleUser   MessageRole = "user"
	RoleAgent  MessageRole = "agent"
	RoleSystem MessageRole = "system"
	RoleTool   MessageRole = "tool"
)

func AllMessageRoles

func AllMessageRoles() []MessageRole

AllMessageRoles returns all valid MessageRole values.

func (MessageRole) MarshalJSON

func (r MessageRole) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler.

func (*MessageRole) UnmarshalJSON

func (r *MessageRole) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler.

func (MessageRole) Valid

func (r MessageRole) Valid() bool

Valid returns true if r is a known MessageRole.

type PartKind

type PartKind string

PartKind represents the kind of a message part.

const (
	PartKindText        PartKind = "text"
	PartKindJSON        PartKind = "json"
	PartKindArtifactRef PartKind = "artifact_ref"
	PartKindError       PartKind = "error"
)

func AllPartKinds

func AllPartKinds() []PartKind

AllPartKinds returns all valid PartKind values.

func (PartKind) MarshalJSON

func (k PartKind) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler.

func (*PartKind) UnmarshalJSON

func (k *PartKind) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler.

func (PartKind) Valid

func (k PartKind) Valid() bool

Valid returns true if k is a known PartKind.

type ProjectedArtifact

type ProjectedArtifact struct {
	ArtifactID   string
	Digest       string
	LogicalRef   string
	ReadOnlyRoot string
	ByteSize     int64
	MediaType    string
}

ProjectedArtifact is the read-only projection returned to a consumer. ReadOnlyRoot is a host path under the broker store — NEVER a peer container path.

type Result

type Result struct {
	SchemaVersion string   `json:"schema_version"`
	ResultID      ResultID `json:"result_id"`
	TaskID        TaskID   `json:"task_id"`
	WorkflowID    string   `json:"workflow_id"`

	// Status must match the task's terminal status.
	Status TaskStatus `json:"status"`

	// OutputMessageID is optional.
	OutputMessageID *MessageID `json:"output_message_id,omitempty"`

	// ArtifactRefs are schema-only transferable artifact references.
	ArtifactRefs []TransferableArtifactRef `json:"artifact_refs,omitempty"`

	// ErrorCode is a stable error code (e.g. ERR_SEQUENCE_GAP).
	ErrorCode string `json:"error_code,omitempty"`
	// ErrorMessage is bounded and must not contain secrets.
	ErrorMessage string `json:"error_message,omitempty"`

	// UsageSummary is optional (tokens/cost strings).
	UsageSummary *UsageSummary `json:"usage_summary,omitempty"`

	ContentDigest string    `json:"content_digest"`
	CreatedAt     time.Time `json:"created_at"`
}

Result is the terminal result of a task.

type ResultID

type ResultID string

ResultID identifies a task result.

func NewResultID

func NewResultID() (ResultID, error)

NewResultID generates a cryptographically random result ID.

func (ResultID) String

func (id ResultID) String() string

String returns the string representation.

func (ResultID) Validate

func (id ResultID) Validate() bool

Validate returns true if the ID is non-empty and has the correct prefix.

type SideDecision

type SideDecision struct {
	Evaluated    bool   `json:"evaluated"`
	Allowed      bool   `json:"allowed"`
	ReasonCode   string `json:"reason_code,omitempty"`
	ReasonDetail string `json:"reason_detail,omitempty"`
}

SideDecision records one side's authorization outcome.

type Store

type Store interface {
	// CreateTask persists a new task. Idempotent on caller_identity+idempotency_key.
	// Returns ERR_IDEMPOTENCY_CONFLICT if the same key exists with a different body.
	CreateTask(ctx context.Context, t Task) error

	// GetTask returns the task by ID, or an error if not found.
	GetTask(ctx context.Context, taskID TaskID) (*Task, error)

	// GetTaskByIdempotencyKey returns the task by caller identity and idempotency key,
	// or nil if not found.
	GetTaskByIdempotencyKey(ctx context.Context, callerIdentity, idempotencyKey string) (*Task, error)

	// CASTask updates a task atomically. Succeeds only if the current
	// generation matches expectedGen. Returns the new generation on success.
	CASTask(ctx context.Context, t Task, expectedGen int64) error

	// AppendMessage appends a message to a task, enforcing contiguous
	// sequence numbers (no gaps). Returns ERR_SEQUENCE_GAP if the sequence
	// is not lastSeq+1.
	AppendMessage(ctx context.Context, msg Message) error

	// ListMessages returns messages for a task with sequence > afterSeq,
	// ordered by sequence ascending.
	ListMessages(ctx context.Context, taskID TaskID, afterSeq int64) ([]Message, error)

	// PutResult stores the terminal result for a task. Only succeeds once
	// per task and the result status must match the task's terminal status.
	PutResult(ctx context.Context, r Result) error

	// GetResult returns the result for a task, or an error if not found.
	GetResult(ctx context.Context, taskID TaskID) (*Result, error)

	// AppendEvent appends an event to a task's event stream. Returns the
	// assigned sequence number.
	AppendEvent(ctx context.Context, ev TaskEvent) (int64, error)

	// ListEvents returns events for a task with sequence > afterSeq,
	// ordered by sequence ascending.
	ListEvents(ctx context.Context, taskID TaskID, afterSeq int64) ([]TaskEvent, error)

	// SubscribeEvents returns a channel of events for a task, replaying
	// existing events with sequence > afterSeq before delivering new events.
	// The channel is closed when the context is cancelled or the task's
	// event stream is closed (terminal event delivered). The returned cancel
	// function unsubscribes the channel; it is safe to call more than once.
	SubscribeEvents(ctx context.Context, taskID TaskID, afterSeq int64) (<-chan TaskEvent, func(), error)
}

Store is the pluggable interface for durable task delegation state. The MemoryStore implementation is suitable for unit tests.

type Task

type Task struct {
	SchemaVersion string `json:"schema_version"`

	TaskID     TaskID `json:"task_id"`
	WorkflowID string `json:"workflow_id"`
	TenantID   string `json:"tenant_id"`

	Caller CallerRef `json:"caller"`
	Callee CalleeRef `json:"callee"`

	// BindingID is the logical binding ID from the signed workflow.
	BindingID string `json:"binding_id"`
	// Capability is the logical name from the signed workflow, e.g. "report.verify".
	Capability string `json:"capability"`
	// Operation is optional; default "" when capability alone is enough.
	Operation string `json:"operation,omitempty"`

	Status TaskStatus `json:"status"`

	// Generation is a monotonically increasing CAS field.
	Generation int64 `json:"generation"`

	// IdempotencyKey + CallerIdentity are required for admission.
	IdempotencyKey string `json:"idempotency_key"`
	CallerIdentity string `json:"caller_identity"`

	// CommunicationSnapshotGeneration pins which workflow snapshot
	// authorized this task.
	CommunicationSnapshotGeneration int64 `json:"communication_snapshot_generation"`

	// InputMessageID is optional until the first message is appended.
	InputMessageID *MessageID `json:"input_message_id,omitempty"`

	// DeadlineAt is an optional time after which the task may be expired.
	DeadlineAt *time.Time `json:"deadline_at,omitempty"`

	// Budget ceilings.
	MaxActiveDurationMs int64  `json:"max_active_duration_ms"`
	MaxCostUsdDecimal   string `json:"max_cost_usd_decimal"`

	// ResultID is set when the task reaches a terminal SUCCEEDED state.
	ResultID *ResultID `json:"result_id,omitempty"`

	// DenialReason is set when status is DENIED.
	DenialReason string `json:"denial_reason,omitempty"`
	// FailureReason is set when status is FAILED.
	FailureReason string `json:"failure_reason,omitempty"`

	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

Task is the authoritative record for a delegated task. NO endpoints, IPs, DNS, ports, capability tokens, or container addresses in any field.

func MaybeExpireTask

func MaybeExpireTask(task *Task, now time.Time) (*Task, bool)

MaybeExpireTask checks if the task is past its deadline and non-terminal. If so, it returns the task with status set to EXPIRED and a CAS-ready copy. The caller must CAS the task to persist the expiration.

Returns (expiredTask, shouldExpire). If shouldExpire is false, the task is either already terminal or not past deadline.

type TaskEvent

type TaskEvent struct {
	EventID    EventID   `json:"event_id"`
	TaskID     TaskID    `json:"task_id"`
	WorkflowID string    `json:"workflow_id"`
	TenantID   string    `json:"tenant_id"`
	Sequence   int64     `json:"sequence"`
	Type       EventType `json:"type"`
	// PayloadDigest is optional.
	PayloadDigest string    `json:"payload_digest,omitempty"`
	CreatedAt     time.Time `json:"created_at"`
}

TaskEvent is an ordered observation event for a task. Delivery is at-least-once; consumers dedupe by task_id+sequence.

type TaskID

type TaskID string

TaskID identifies a task.

func NewTaskID

func NewTaskID() (TaskID, error)

NewTaskID generates a cryptographically random task ID.

func (TaskID) String

func (id TaskID) String() string

String returns the string representation.

func (TaskID) Validate

func (id TaskID) Validate() bool

Validate returns true if the ID is non-empty and has the correct prefix.

type TaskOutbox

type TaskOutbox struct {
	// contains filtered or unexported fields
}

TaskOutbox wraps a Store so that a terminal task-CAS and its result/event commit with the same atomicity contract as trigger.Outbox. If any step fails, the caller sees the error and the mutation is treated as uncommitted.

Ordering: state CAS → PutResult → AppendEvent. If the CAS fails, result and event are never written. If the result write fails, the event is never appended. If the event append fails, the state mutation is treated as uncommitted — the caller must retry; the idempotent CAS will either succeed (fresh attempt) or conflict (already committed).

func NewTaskOutbox

func NewTaskOutbox(store Store) *TaskOutbox

NewTaskOutbox creates a TaskOutbox wrapping the given Store.

func (*TaskOutbox) CommitTerminal

func (o *TaskOutbox) CommitTerminal(
	ctx context.Context,
	task Task,
	expectedGen int64,
	result *Result,
	ev TaskEvent,
) (int64, error)

CommitTerminal atomically transitions a task to a terminal state, stores the result, and appends the terminal event.

Parameters:

  • task: the current task state (used for transition validation + status)
  • expectedGen: the expected generation for CAS
  • result: the terminal result (must have matching Status)
  • ev: the terminal TaskEvent (Type must be one of: SUCCEEDED, FAILED, CANCELLED, EXPIRED)

If the event type is TASK_DENIED, no result is stored (denied tasks don't produce results — they produce a denial reason on the task status).

Returns the sequence number of the appended event.

type TaskStatus

type TaskStatus int

TaskStatus represents the lifecycle status of a delegated task.

const (
	TaskStatusPending   TaskStatus = iota // 0
	TaskStatusAdmitted                    // 1
	TaskStatusRunning                     // 2
	TaskStatusSucceeded                   // 3
	TaskStatusFailed                      // 4
	TaskStatusCancelled                   // 5
	TaskStatusExpired                     // 6
	TaskStatusDenied                      // 7
)

func AllTaskStatuses

func AllTaskStatuses() []TaskStatus

AllTaskStatuses returns all valid TaskStatus values.

func (TaskStatus) IsTerminal

func (s TaskStatus) IsTerminal() bool

IsTerminal returns true for terminal task statuses.

func (TaskStatus) MarshalJSON

func (s TaskStatus) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler.

func (TaskStatus) String

func (s TaskStatus) String() string

String returns the string representation.

func (*TaskStatus) UnmarshalJSON

func (s *TaskStatus) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler.

func (TaskStatus) Valid

func (s TaskStatus) Valid() bool

Valid returns true if s is a known TaskStatus.

type TaskWaiter

type TaskWaiter struct {
	// contains filtered or unexported fields
}

TaskWaiter provides wait/subscribe APIs on top of a Store so that a parent orchestration can suspend, checkpoint, and resume without polling.

func NewTaskWaiter

func NewTaskWaiter(store Store) *TaskWaiter

NewTaskWaiter creates a TaskWaiter wrapping the given Store.

func (*TaskWaiter) Subscribe

func (w *TaskWaiter) Subscribe(
	ctx context.Context,
	taskID TaskID,
	afterSeq int64,
) (<-chan TaskEvent, func(), error)

Subscribe returns a channel of events for a task, replaying existing events with sequence > afterSeq. The returned cancel function unsubscribes the channel and closes it safely.

The channel is closed when:

  • The context is cancelled.
  • A terminal event is delivered.
  • The cancel function is called.

func (*TaskWaiter) WaitTerminal

func (w *TaskWaiter) WaitTerminal(
	ctx context.Context,
	taskID TaskID,
	afterSeq int64,
) (Task, []TaskEvent, error)

WaitTerminal blocks until the task reaches a terminal status, then returns the task and all events with sequence > afterSeq. The caller passes a cursor (afterSeq) to resume from a known position.

If the context is cancelled before the task becomes terminal, the method returns the context error.

type TransferableArtifactRef

type TransferableArtifactRef struct {
	ArtifactID        string         `json:"artifact_id"`
	Digest            string         `json:"digest"` // SHA-256 hex
	WorkflowID        string         `json:"workflow_id"`
	ProducerRunID     string         `json:"producer_run_id"`
	ProducerAttemptID string         `json:"producer_attempt_id"`
	ProducerTaskID    string         `json:"producer_task_id"`
	MediaType         string         `json:"media_type"`
	ByteSize          int64          `json:"byte_size"`
	Classification    Classification `json:"classification"`
	// Audience is the list of logical package/deployment IDs allowed to read.
	Audience  []string  `json:"audience"`
	ExpiresAt time.Time `json:"expires_at"`
	// LogicalRef is a relative path (e.g. "output.json").
	LogicalRef string `json:"logical_ref"`
}

TransferableArtifactRef is a reduced grant model from B32 simplification. NEVER: storage URL, raw credential, host path, shared mount path.

type TransitionError

type TransitionError struct {
	Resource  string `json:"resource"`
	FromState string `json:"from_state"`
	ToState   string `json:"to_state"`
	Message   string `json:"message"`
}

TransitionError is returned when an invalid state transition is attempted.

func (*TransitionError) Error

func (e *TransitionError) Error() string

Error returns the error message.

type UsageSummary

type UsageSummary struct {
	InputTokens     int64  `json:"input_tokens"`
	OutputTokens    int64  `json:"output_tokens"`
	TotalCostUsdStr string `json:"total_cost_usd_str,omitempty"`
}

UsageSummary captures optional token and cost usage.

type ValidationError

type ValidationError struct {
	Field   string `json:"field"`
	Message string `json:"message"`
}

ValidationError reports a schema or content validation failure.

func (*ValidationError) Error

func (e *ValidationError) Error() string

Error returns the error message.

type WorkflowDelegationBinding

type WorkflowDelegationBinding struct {
	BindingID            string   `json:"binding_id" yaml:"binding_id"`
	Operation            string   `json:"operation,omitempty" yaml:"operation,omitempty"`
	CalleePackageName    string   `json:"callee_package_name" yaml:"package_name"`
	CalleePackageVersion string   `json:"callee_package_version" yaml:"package_version"`
	CalleeBundleDigest   string   `json:"callee_bundle_digest" yaml:"bundle_digest"`
	CallerPackageName    string   `json:"caller_package_name,omitempty" yaml:"caller_package_name,omitempty"`
	MaxDataClass         string   `json:"max_data_class" yaml:"max_data_class"`
	ArtifactAudience     []string `json:"artifact_audience,omitempty" yaml:"artifact_audience,omitempty"`
	DeadlineMs           int64    `json:"deadline_ms,omitempty" yaml:"deadline_ms,omitempty"`
	MaxCostUSDDecimal    string   `json:"max_cost_usd_decimal,omitempty" yaml:"max_cost_usd_decimal,omitempty"`
}

WorkflowDelegationBinding represents a single delegation binding within a signed workflow snapshot. It pins the logical capability name, the exact callee package digest, optional operation, data classification ceiling, artifact audience, deadline, and budget.

type WriterKind

type WriterKind string

WriterKind discriminates who is writing a message.

const (
	WriterAgent   WriterKind = "agent"   // Agent SDK
	WriterRuntime WriterKind = "runtime" // Trusted runtime / gateway
)

func (WriterKind) Valid

func (w WriterKind) Valid() bool

Valid returns true if w is a known WriterKind.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL