decisionreceipt

package
v0.57.11 Latest Latest
Warning

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

Go to latest
Published: Nov 13, 2026 License: Apache-2.0 Imports: 24 Imported by: 0

Documentation

Overview

Package decisionreceipt verifies the platform's signed decision receipt for one StackKits operation and spends it exactly once.

The receipt is the person's confirmation the platform's decision ledger signs (kombify-Gateway pull request #3129): a compact JWS of type kombify.decision-receipt.v1 over a stackkit_operation subject. A consumer accepts it only when the signature verifies against a published purpose key for decision receipts, the receipt is for this store, the answer allowed this exact call once, the answering party was a person on a surface other than equipment, the receipt is inside its time window, and the intent digest equals the digest recomputed from the consumer's own resolved operation, target, arguments, revision and acting principal.

Index

Constants

View Source
const (
	// OperationBackupRestore activates one verified staged restore over the
	// live volumes.
	OperationBackupRestore = "backup_restore"
	// OperationApplicationRemove removes one application with its data.
	OperationApplicationRemove = "application_remove"
	// OperationAdvancedChangeSetApply applies one Owner-signed Advanced
	// change set.
	OperationAdvancedChangeSetApply = "advanced_change_set_apply"
	// OperationInstallationRemove removes the installation with its data.
	OperationInstallationRemove = "installation_remove"
)

The ledger's catalogue operation ids for the first slice of operations whose confirmation a decision receipt carries.

The ledger accepts a lowercase id with underscores, so these are not the dotted StackKits catalogue ids of the operations themselves: the ledger's id names the operation, the StackKits catalogue id names the command that implements it. The two are bound in FirstSlice.

View Source
const (
	PrincipalPerson        = "person"
	PrincipalAgentInstance = "agent_instance"
)

The principal kinds the ledger accepts.

View Source
const (
	TargetApplication    = "application"
	TargetOwnApplication = "own-application"
	TargetRestore        = "restore"
	TargetChangeSet      = "change-set"
)

The target kinds of the first slice (docs/DECISION-RECEIPTS.md, "Binding definition"). The installation itself has no kind: its reference is the node's own.

View Source
const (
	// RefusalInvalid covers the signature, the shape, the outcome and the
	// answering party.
	RefusalInvalid = "stackkit_receipt_invalid"
	// RefusalExpired covers a receipt outside its time window.
	RefusalExpired = "stackkit_receipt_expired"
	// RefusalSpent covers a receipt that was already used on this node.
	RefusalSpent = "stackkit_receipt_spent"
	// RefusalTargetMismatch covers a receipt that binds another operation,
	// target, digest, revision or acting principal.
	RefusalTargetMismatch = "stackkit_receipt_target_mismatch"
)

The refusal names the platform binds for every repository in this work package. A consumer answers with one of these names and nothing else.

View Source
const (
	// ReceiptType is the JWS protected header type of a decision receipt.
	ReceiptType = "kombify.decision-receipt.v1"
	// SubjectStore is the only ledger subject this consumer admits. A RIL
	// action receipt names another store and is refused here; that check is
	// what keeps one receipt from passing for the other.
	SubjectStore = "stackkit_operation"
	// IntentDigestDomain separates the intent digest from every other digest.
	IntentDigestDomain = "kombify.decision-intent.digest.v1"
	// RiskDestructive is the risk the ledger records for a StackKits operation.
	RiskDestructive = "destructive"
	// OutcomeAllowed and OptionAllowOnce are the only answer profile a
	// consumer admits.
	OutcomeAllowed  = "allowed"
	OptionAllowOnce = "allow_once"
	// AnsweredByPerson is the only answering party.
	AnsweredByPerson = "person"
	// SurfaceEquipment is the surface a person's answer may never carry.
	SurfaceEquipment = "equipment"
	// LifetimeOnce is the only lifetime scope.
	LifetimeOnce = "once"
)

The receipt contract the platform's ledger signs.

View Source
const ArgumentsDigestDomain = "kombify.stackkit-operation-arguments.digest.v1"

ArgumentsDigestDomain separates the digest of one operation's canonical arguments from every other digest. The digest follows the platform's one digest convention (the ledger's intent and record digests): SHA-256 over the domain, a newline, the canonical JSON and a newline.

View Source
const MaxReceiptLifetime = 900 * time.Second

MaxReceiptLifetime is the longest window a receipt may carry: the ledger admits a stackkit_operation decision for at most 900 seconds, and a consumer refuses a longer window rather than trusting the producer for it.

Variables

This section is empty.

Functions

func AdvancedChangeSetApplyArguments

func AdvancedChangeSetApplyArguments(changeSetID string) map[string]any

AdvancedChangeSetApplyArguments is the canonical argument object of applying one Advanced change set. The digest of the stored change-set bytes is the operation's plan digest (planSha256), not an argument; the candidate and capability paths are host local and never enter a digest, because the Owner-signed change set those bytes hold already binds what they name.

func ApplicationRemoveArguments

func ApplicationRemoveArguments(workload string, deleteData bool) map[string]any

ApplicationRemoveArguments is the canonical argument object of removing one application workload with its data.

func ArgumentsDigest

func ArgumentsDigest(arguments map[string]any) (string, error)

ArgumentsDigest is the 64-hex SHA-256 of one operation's canonical arguments: SHA-256 over ArgumentsDigestDomain, a newline, the canonical JSON of the argument object (the ledger's canonicalDecisionJson form) and a newline. The producer of a decision and the consumer of its receipt build the same argument object; the ledger never sees the arguments themselves, only this digest.

func BackupRestoreArguments

func BackupRestoreArguments(resultID, workload, marketplaceService, operationID string) map[string]any

BackupRestoreArguments is the canonical argument object of one restore activation.

func InstallationRemoveArguments

func InstallationRemoveArguments(deleteData, purge bool) map[string]any

InstallationRemoveArguments is the canonical argument object of removing the installation with its data.

func IntentDigest

func IntentDigest(facts Facts) (string, error)

IntentDigest recomputes the digest the ledger signed for one decision.

The intent is the operation, its target, its risk, and the parameters that bind the digests, the revision and the acting principal; planSha256 is a member only when the operation has a plan. The digest is the domain-separated SHA-256 of the canonical JSON of that intent, exactly as the ledger computes it.

func IsFirstSlice

func IsFirstSlice(ledgerID string) bool

IsFirstSlice reports whether a ledger operation id is in the first slice.

func OwnApplicationRemoveArguments

func OwnApplicationRemoveArguments(name string, deleteData, keepSecrets bool, operationID string) map[string]any

OwnApplicationRemoveArguments is the canonical argument object of removing one own application with its data.

func ValidDigest

func ValidDigest(value string) bool

ValidDigest reports whether a value is a hex SHA-256 digest.

Types

type Facts

type Facts struct {
	// Operation is the ledger's catalogue operation id.
	Operation string
	// Resource is the exact target reference.
	Resource string
	// ArgumentsSHA256 is the 64-hex digest of the canonical arguments.
	ArgumentsSHA256 string
	// PlanSHA256 is the 64-hex digest of the reviewed plan, empty when the
	// operation has none.
	PlanSHA256 string
	// Revision is the revision the operation was resolved against.
	Revision string
	// Principal is the acting principal the caller's entry point authenticated.
	Principal Principal
}

Facts are the resolved facts one receipt must bind: they are exactly the members of the intent the ledger signs.

type KeySet

type KeySet struct {
	Keys []PurposeKey
}

KeySet is the published decision-receipt key set a consumer verifies with.

type NodeIdentity

type NodeIdentity struct {
	TenantID string
	ServerID string
	// OwnerID is the platform subject of the installation's Owner, pinned at
	// the hosted enrollment. It is the only id a person principal may carry
	// (docs/DECISION-RECEIPTS.md, "Acting principal"); empty when the
	// enrollment named no Owner, and then no person principal is admitted.
	OwnerID string
}

NodeIdentity is the node's own enrolled identity: the tenant and the server the platform enrolled this node as. It is read only from root-owned node custody, never from an argument, so a receipt bound to one node is refused on its twin.

type Operation

type Operation struct {
	// LedgerID is the operation id the ledger signs in the intent.
	LedgerID string
	// CatalogueID is the StackKits catalogue id of the operation.
	CatalogueID string
	// MCPTool is the MCP tool that reaches the operation.
	MCPTool string
}

Operation is one first-slice operation: the ledger's id, the StackKits catalogue id of the command that implements it, and the MCP tool that reaches it (empty when no MCP tool does).

func FirstSlice

func FirstSlice() []Operation

FirstSlice is the first slice of operations whose confirmation a signed decision receipt carries instead of a self-asserted boolean.

type Principal

type Principal struct {
	Kind string `json:"kind"`
	ID   string `json:"id"`
}

Principal is the acting principal a decision is bound to. The ledger signs it inside the intent, so a consumer can only recompute the intent digest when it knows the principal the caller's entry point authenticated.

func EnrolledOwnerPrincipal

func EnrolledOwnerPrincipal() (Principal, error)

EnrolledOwnerPrincipal is the person principal of this node's enrolled Owner, read from root-owned node custody: the one person id a filer files for the Owner (docs/DECISION-RECEIPTS.md, "Acting principal").

func EnvelopeAgentPrincipal

func EnvelopeAgentPrincipal(linkRef, instance, account string) (Principal, error)

EnvelopeAgentPrincipal is the agent instance principal of one Gateway agent envelope, spelled as the platform files it: gateway:<link ref>:<agent instance>/<account>.

type PurposeKey

type PurposeKey struct {
	Kid    string   `json:"kid"`
	Kty    string   `json:"kty"`
	Crv    string   `json:"crv"`
	X      string   `json:"x"`
	Y      string   `json:"y,omitempty"`
	Alg    string   `json:"alg,omitempty"`
	Use    string   `json:"use,omitempty"`
	KeyOps []string `json:"key_ops,omitempty"`
	Ext    *bool    `json:"ext,omitempty"`
}

PurposeKey is one published public key of the decision-receipt purpose.

type Receipt

type Receipt struct {
	ReceiptID     string
	DecisionID    string
	OperationID   string
	SubjectStore  string
	IssuedAt      time.Time
	ExpiresAt     time.Time
	Outcome       string
	OptionKind    string
	AnsweredBy    Principal
	Surface       string
	Assurance     string
	IntentDigest  string
	LifetimeScope string
}

Receipt is one verified decision receipt.

func Admit

func Admit(token string, request Request, now time.Time) (*Receipt, error)

Admit verifies one receipt for one first-slice call on this node and spends it before the call's first effect.

Everything it trusts comes from node custody (custody.go): the purpose keys the privileged refresher writes, inside their window; the node's enrolled identity, which every target reference carries; and the node-level spent record. A process that cannot read the keys or the identity cannot verify and refuses the receipt; a process that cannot write the spent record refuses the call.

func Verify

func Verify(token string, facts Facts, keys *KeySet, now time.Time) (*Receipt, error)

Verify checks one compact JWS receipt against the consumer's own resolved facts and the published purpose keys. It returns the receipt when every condition holds, and a named refusal otherwise.

The refusals come in the contract's order (kombify-workspace scripts/check-decision-contract.mjs, stackkitReceiptConsumerSemantics): stackkit_receipt_invalid first (the signature against a published purpose key for decision receipts, the type, the store, the expiry member, the answer allowing this call once, a person answering on a surface other than equipment, a window of at most 900 seconds, and a clock not before issuedAt), then stackkit_receipt_target_mismatch (the intent digest recomputed here from the consumer's own operation, target, arguments, plan, revision and acting principal), then stackkit_receipt_expired (the clock at or after expiresAt). stackkit_receipt_spent comes last, from the spent record (Admit).

type Record

type Record struct {
	ReceiptID  string    `json:"receiptId"`
	DecisionID string    `json:"decisionId"`
	Operation  string    `json:"operation"`
	Resource   string    `json:"resource"`
	SpentAt    time.Time `json:"spentAt"`
}

Record is what the spent record stores about one receipt. It holds the receipt identity and the operation it was spent on, never the receipt itself: the receipt is a bearer credential and does not belong in a file.

type Refusal

type Refusal struct {
	Name    string
	Message string
}

Refusal is a named refusal. The name is the contract; the message only explains it.

func (*Refusal) Error

func (r *Refusal) Error() string

type Request

type Request struct {
	Operation string
	Target    Target
	Arguments map[string]any
	// PlanSHA256 is the 64-hex digest of the reviewed plan, or empty when the
	// operation has none.
	PlanSHA256 string
	Revision   string
	Principal  Principal
}

Request is one first-slice call as its command resolved it: the operation, the target on this node, the canonical arguments, the plan digest where the operation has one, the revision, and the acting principal.

func (Request) Facts

func (r Request) Facts(node NodeIdentity) (Facts, error)

Facts resolves one request on this node into the facts its receipt binds.

type Target

type Target struct {
	Kind string
	Ref  string
}

Target is what one operation acts on, without the node: the node is added from custody, never from the caller. An empty Kind names the installation.

func (Target) Reference

func (t Target) Reference(node NodeIdentity) (string, error)

Reference is the exact target reference the ledger signs as intent.resource: stackkit:<server id> for the installation, and stackkit:<server id>:<kind>:<ref> for everything on it.

Jump to

Keyboard shortcuts

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