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
- func AdvancedChangeSetApplyArguments(changeSetID string) map[string]any
- func ApplicationRemoveArguments(workload string, deleteData bool) map[string]any
- func ArgumentsDigest(arguments map[string]any) (string, error)
- func BackupRestoreArguments(resultID, workload, marketplaceService, operationID string) map[string]any
- func InstallationRemoveArguments(deleteData, purge bool) map[string]any
- func IntentDigest(facts Facts) (string, error)
- func IsFirstSlice(ledgerID string) bool
- func OwnApplicationRemoveArguments(name string, deleteData, keepSecrets bool, operationID string) map[string]any
- func ValidDigest(value string) bool
- type Facts
- type KeySet
- type NodeIdentity
- type Operation
- type Principal
- type PurposeKey
- type Receipt
- type Record
- type Refusal
- type Request
- type Target
Constants ¶
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.
const ( PrincipalPerson = "person" PrincipalAgentInstance = "agent_instance" )
The principal kinds the ledger accepts.
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.
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.
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.
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.
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 ¶
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 ¶
ApplicationRemoveArguments is the canonical argument object of removing one application workload with its data.
func ArgumentsDigest ¶
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 ¶
InstallationRemoveArguments is the canonical argument object of removing the installation with its data.
func IntentDigest ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 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.