flowcore

package
v19.4.0 Latest Latest
Warning

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

Go to latest
Published: Sep 16, 2026 License: MIT Imports: 58 Imported by: 0

Documentation

Index

Constants

View Source
const (
	WorkflowStateUnknown      = autocore.WorkflowStateUnknown
	WorkflowStateRunning      = autocore.WorkflowStateRunning
	WorkflowStateCompleted    = autocore.WorkflowStateCompleted
	WorkflowStateFailed       = autocore.WorkflowStateFailed
	WorkflowStateCanceled     = autocore.WorkflowStateCanceled
	WorkflowStateTimedOut     = autocore.WorkflowStateTimedOut
	WorkflowStateSystemFailed = autocore.WorkflowStateSystemFailed
)

Variables

View Source
var (
	// ErrInvalidChannelName is returned by RunWorkflow when the input carries a
	// channel whose name the caller is not allowed to use.
	ErrInvalidChannelName = errors.New("invalid channel name")
	// ErrInvalidValue is returned by the send methods for a value they refuse to
	// put on a channel.
	ErrInvalidValue = errors.New("invalid value")
	// ErrInvalidChannelToken is returned by the send methods when the channel token
	// cannot be parsed or carries unusable claims. The token's signature is not
	// checked there, so this never means "forged".
	ErrInvalidChannelToken = errors.New("invalid channel token")
	// ErrInvalidTokenType is a token whose `typ` header is not the one the parser
	// accepts, so it was minted as a different kind of token.
	ErrInvalidTokenType = errors.New("invalid token type")
	// ErrInvalidWorkflowToken is returned by the methods that manage a workflow
	// when the workflow token does not verify against the workflow it addresses.
	// Unlike a channel token it is verified where it is presented, so this does
	// mean the bearer holds no capability for that workflow.
	ErrInvalidWorkflowToken = errors.New("invalid workflow token")
	// ErrInvalidTokenBinding is returned by RunWorkflow when a submission
	// deduplicates against an existing workflow but presents a binding other than
	// the one that workflow registered, so it is not entitled to its tokens.
	ErrInvalidTokenBinding = errors.New("invalid token binding")
	// ErrTokenExpiryPassed is returned by RunWorkflow when it deduplicates against a
	// workflow whose recorded token expiry has already passed, so every token it
	// could mint for that workflow would be dead on arrival.
	ErrTokenExpiryPassed = errors.New("workflow token expiry has passed")
	// ErrMissingSigningSecret is returned by RunWorkflow when the workflow it
	// deduplicated against holds no signing secret, so no usable token can be
	// minted for it, and by the signal promotion interceptor when the workflow a
	// channel signal addresses holds none, so no token can be verified against it.
	ErrMissingSigningSecret = errors.New("workflow has no signing secret")
)
View Source
var (
	// ErrWorkflowNotFound is returned by GetWorkflow when no workflow exists for
	// the given key.
	ErrWorkflowNotFound = autocore.ErrWorkflowNotFound
	// ErrReservedIdempotencyKeyPrefix is returned by RunWorkflow when the caller's
	// idempotency key uses the prefix the engine keeps for itself.
	ErrReservedIdempotencyKeyPrefix = autocore.ErrReservedIdempotencyKeyPrefix
	// ErrWorkflowNotRunning is returned by the send paths when the addressed
	// workflow has already reached a terminal state, and ErrSignalLimitExceeded when
	// it has spent its signal history budget. Both are permanent for that workflow:
	// re-sending cannot succeed.
	ErrWorkflowNotRunning  = autocore.ErrWorkflowNotRunning
	ErrSignalLimitExceeded = autocore.ErrSignalLimitExceeded
	// ErrInvalidWorkflowKey is returned by GetWorkflow when the key is malformed.
	ErrInvalidWorkflowKey = autocore.ErrInvalidWorkflowKey
	// ErrMetadataTooLarge is returned by GetWorkflow when metadata is too large.
	ErrMetadataTooLarge = autocore.ErrMetadataTooLarge
	// ErrInputTooLarge is returned by RunWorkflow when the workflow definition and
	// the arguments together marshal past the workflow input bound.
	ErrInputTooLarge = autocore.ErrInputTooLarge
	// ErrIdempotencyKeyRetired is returned by RunWorkflow when the key is pinned to
	// a workflow whose data has been retired. Permanent until the entry expires.
	ErrIdempotencyKeyRetired = autocore.ErrIdempotencyKeyRetired
)
View Source
var File_internal_flowcore_channel_token_proto protoreflect.FileDescriptor
View Source
var File_internal_flowcore_engine_proto protoreflect.FileDescriptor
View Source
var File_internal_flowcore_workflow_token_proto protoreflect.FileDescriptor

Functions

func NewSensitiveBytes added in v19.2.0

func NewSensitiveBytes(b []byte) starlark.Value

func NewSensitiveString added in v19.2.0

func NewSensitiveString(s string) starlark.Value

func NonRetryable added in v19.4.0

func NonRetryable(err error) error

NonRetryable marks err as a failure no repeat can change, so the activity carrying it fails at once rather than spending its retry budget. It keeps err's message, so what the flow reports is the failure that occurred. A nil err stays nil, so a caller can mark whatever it is about to return.

Types

type Actor added in v19.4.0

type Actor struct {
	RegisteredClaimSubject string `protobuf:"bytes,1,opt,name=registered_claim_subject,json=sub" json:"registered_claim_subject,omitempty"`
	// contains filtered or unexported fields
}

func (*Actor) Descriptor deprecated added in v19.4.0

func (*Actor) Descriptor() ([]byte, []int)

Deprecated: Use Actor.ProtoReflect.Descriptor instead.

func (*Actor) GetRegisteredClaimSubject added in v19.4.0

func (x *Actor) GetRegisteredClaimSubject() string

func (*Actor) ProtoMessage added in v19.4.0

func (*Actor) ProtoMessage()

func (*Actor) ProtoReflect added in v19.4.0

func (x *Actor) ProtoReflect() protoreflect.Message

func (*Actor) Reset added in v19.4.0

func (x *Actor) Reset()

func (*Actor) String added in v19.4.0

func (x *Actor) String() string

type ChannelSend added in v19.3.0

type ChannelSend struct {
	IdempotencyKey string
	Value          *pkg_autoflow.Value
}

ChannelSend is a single value bound for a channel, together with the idempotency key that deduplicates it.

type ChannelSignalPayload added in v19.2.0

type ChannelSignalPayload struct {
	Token   string `protobuf:"bytes,1,opt,name=token" json:"token,omitempty"`
	Payload []byte `protobuf:"bytes,2,opt,name=payload" json:"payload,omitempty"`
	// contains filtered or unexported fields
}

func (*ChannelSignalPayload) Descriptor deprecated added in v19.2.0

func (*ChannelSignalPayload) Descriptor() ([]byte, []int)

Deprecated: Use ChannelSignalPayload.ProtoReflect.Descriptor instead.

func (*ChannelSignalPayload) GetPayload added in v19.2.0

func (x *ChannelSignalPayload) GetPayload() []byte

func (*ChannelSignalPayload) GetToken added in v19.2.0

func (x *ChannelSignalPayload) GetToken() string

func (*ChannelSignalPayload) ProtoMessage added in v19.2.0

func (*ChannelSignalPayload) ProtoMessage()

func (*ChannelSignalPayload) ProtoReflect added in v19.2.0

func (x *ChannelSignalPayload) ProtoReflect() protoreflect.Message

func (*ChannelSignalPayload) Reset added in v19.2.0

func (x *ChannelSignalPayload) Reset()

func (*ChannelSignalPayload) String added in v19.2.0

func (x *ChannelSignalPayload) String() string

type ChannelTokenClaims added in v19.2.0

type ChannelTokenClaims struct {
	RegisteredClaimIssuer    string   `protobuf:"bytes,1,opt,name=registered_claim_issuer,json=iss" json:"registered_claim_issuer,omitempty"`
	RegisteredClaimAudience  []string `protobuf:"bytes,2,rep,name=registered_claim_audience,json=aud" json:"registered_claim_audience,omitempty"`
	RegisteredClaimExpiresAt float64  `protobuf:"fixed64,3,opt,name=registered_claim_expires_at,json=exp" json:"registered_claim_expires_at,omitempty"`
	RegisteredClaimNotBefore float64  `protobuf:"fixed64,4,opt,name=registered_claim_not_before,json=nbf" json:"registered_claim_not_before,omitempty"`
	RegisteredClaimIssuedAt  float64  `protobuf:"fixed64,5,opt,name=registered_claim_issued_at,json=iat" json:"registered_claim_issued_at,omitempty"`
	RegisteredClaimSubject   *string  `protobuf:"bytes,6,opt,name=registered_claim_subject,json=sub" json:"registered_claim_subject,omitempty"`
	ShardId                  int32    `protobuf:"varint,7,opt,name=shard_id" json:"shard_id,omitempty"`
	WorkflowId               string   `protobuf:"bytes,8,opt,name=workflow_id,json=wf_id" json:"workflow_id,omitempty"`
	ChannelName              string   `protobuf:"bytes,9,opt,name=channel_name" json:"channel_name,omitempty"`
	Act                      *Actor   `protobuf:"bytes,10,opt,name=act" json:"act,omitempty"`
	// contains filtered or unexported fields
}

func (*ChannelTokenClaims) Descriptor deprecated added in v19.2.0

func (*ChannelTokenClaims) Descriptor() ([]byte, []int)

Deprecated: Use ChannelTokenClaims.ProtoReflect.Descriptor instead.

func (*ChannelTokenClaims) GetAct added in v19.4.0

func (x *ChannelTokenClaims) GetAct() *Actor

func (*ChannelTokenClaims) GetAudience added in v19.2.0

func (c *ChannelTokenClaims) GetAudience() (jwt.ClaimStrings, error)

func (*ChannelTokenClaims) GetChannelName added in v19.2.0

func (x *ChannelTokenClaims) GetChannelName() string

func (*ChannelTokenClaims) GetExpirationTime added in v19.2.0

func (c *ChannelTokenClaims) GetExpirationTime() (*jwt.NumericDate, error)

func (*ChannelTokenClaims) GetIssuedAt added in v19.2.0

func (c *ChannelTokenClaims) GetIssuedAt() (*jwt.NumericDate, error)

func (*ChannelTokenClaims) GetIssuer added in v19.2.0

func (c *ChannelTokenClaims) GetIssuer() (string, error)

func (*ChannelTokenClaims) GetNotBefore added in v19.2.0

func (c *ChannelTokenClaims) GetNotBefore() (*jwt.NumericDate, error)

func (*ChannelTokenClaims) GetRegisteredClaimAudience added in v19.2.0

func (x *ChannelTokenClaims) GetRegisteredClaimAudience() []string

func (*ChannelTokenClaims) GetRegisteredClaimExpiresAt added in v19.2.0

func (x *ChannelTokenClaims) GetRegisteredClaimExpiresAt() float64

func (*ChannelTokenClaims) GetRegisteredClaimIssuedAt added in v19.2.0

func (x *ChannelTokenClaims) GetRegisteredClaimIssuedAt() float64

func (*ChannelTokenClaims) GetRegisteredClaimIssuer added in v19.2.0

func (x *ChannelTokenClaims) GetRegisteredClaimIssuer() string

func (*ChannelTokenClaims) GetRegisteredClaimNotBefore added in v19.2.0

func (x *ChannelTokenClaims) GetRegisteredClaimNotBefore() float64

func (*ChannelTokenClaims) GetRegisteredClaimSubject added in v19.4.0

func (x *ChannelTokenClaims) GetRegisteredClaimSubject() string

func (*ChannelTokenClaims) GetShardId added in v19.2.0

func (x *ChannelTokenClaims) GetShardId() int32

func (*ChannelTokenClaims) GetSubject added in v19.2.0

func (c *ChannelTokenClaims) GetSubject() (string, error)

GetSubject reports an absent subject as the empty string, which is what a workflow channel token carries: it is about no principal.

func (*ChannelTokenClaims) GetWorkflowId added in v19.2.0

func (x *ChannelTokenClaims) GetWorkflowId() string

func (*ChannelTokenClaims) MarshalJSON added in v19.2.0

func (c *ChannelTokenClaims) MarshalJSON() ([]byte, error)

func (*ChannelTokenClaims) ProtoMessage added in v19.2.0

func (*ChannelTokenClaims) ProtoMessage()

func (*ChannelTokenClaims) ProtoReflect added in v19.2.0

func (x *ChannelTokenClaims) ProtoReflect() protoreflect.Message

func (*ChannelTokenClaims) Reset added in v19.2.0

func (x *ChannelTokenClaims) Reset()

func (*ChannelTokenClaims) String added in v19.2.0

func (x *ChannelTokenClaims) String() string

func (*ChannelTokenClaims) UnmarshalJSON added in v19.2.0

func (c *ChannelTokenClaims) UnmarshalJSON(b []byte) error

func (*ChannelTokenClaims) WorkflowKey added in v19.2.0

func (c *ChannelTokenClaims) WorkflowKey() (autocore.WorkflowKey, error)

type ChannelTokens added in v19.4.0

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

ChannelTokens is the channel token capability: minting, parsing, and the exchange a module uses to hand a channel to somebody who is not an AutoFlow module agent. One instance serves both the engine and the built-in modules, which are constructed before it, so there is a single configuration rather than two that could drift apart.

Each kind of token gets a parser configured with its own audience, which is what stops a token of one kind being spent as the other. Issuer and audiences are not configurable: an overlap between them would collapse that separation.

verify accepts either audience. Signal promotion uses it, because both send endpoints write signals and neither kind is more trusted once the signature holds.

func NewChannelTokens added in v19.4.0

func NewChannelTokens(client autocore.Client, validator protovalidate.Validator, signingMethod *jwt.SigningMethodHMAC) *ChannelTokens

func (*ChannelTokens) ExchangeChannelTokens added in v19.4.0

func (c *ChannelTokens) ExchangeChannelTokens(
	ctx context.Context,
	agentKey api.AgentKey,
	tokens []*pkg_autoflow.ChannelToken,
) ([]*pkg_autoflow.ChannelToken, error)

ExchangeChannelTokens trades the agent channel tokens of one action invocation for workflow channel tokens addressing the same channels, so that agentKey can hand the capabilities to a party that is no AutoFlow module agent and could therefore never spend the tokens it holds. The gitlab module's post_value action does this, and puts the results in the request body it posts.

An exchanged token is about no principal, expires no later than the token it came from, and records agentKey as its actor. That last one is provenance only: what authorizes a send with it is the workflow token presented alongside. See https://www.rfc-editor.org/rfc/rfc8693#section-4.1

This is also the only way anything outside the workflow reaches a channel the flow created for itself. What a caller may name is bounded in RunWorkflow; what a flow may hand to an action is not, and passing the channel to the action is the flow's consent.

Unlike a send, this mints new capabilities out of what it is given, so it verifies each token here instead of leaving that to signal promotion. The workflow whose secret does the verifying is named by the token before it is verified, which is safe because the caller is an in-process built-in module and the subject check has already tied the token to it. Exposing this to agentk-class agents over the module API would make it an unauthenticated read amplifier, and would need a check that does not depend on the token's own claims.

func (*ChannelTokens) NewAgentMinter added in v19.4.0

func (c *ChannelTokens) NewAgentMinter(signingKey []byte, expiresAt time.Time) channelTokenMinter

func (*ChannelTokens) NewWorkflowMinter added in v19.4.0

func (c *ChannelTokens) NewWorkflowMinter(signingKey []byte, expiresAt time.Time) *jwtWorkflowChannelTokenMinter

func (*ChannelTokens) ParseVerifyValidate added in v19.4.0

func (c *ChannelTokens) ParseVerifyValidate(
	token string,
	signingKey []byte,
	expectedChannelName string,
	expectedShardID autocore.ShardID,
	expectedWorkflowID autocore.WorkflowID,
	at time.Time,
) (*ChannelTokenClaims, error)

ParseVerifyValidate verifies a channel token of either kind against the workflow's signing key, and checks it addresses the channel of the workflow it was presented for. Signal promotion is its only caller, and it has no principal to check a subject against.

at is when exp and nbf are judged from, rather than the wall clock, because the caller must reach the same verdict however often it is re-run.

func (*ChannelTokens) ParseVerifyValidateAgent added in v19.4.0

func (c *ChannelTokens) ParseVerifyValidateAgent(
	token string,
	signingKey []byte,
	agentKey api.AgentKey,
	expectedShardID autocore.ShardID,
	expectedWorkflowID autocore.WorkflowID,
) (*ChannelTokenClaims, error)

ParseVerifyValidateAgent verifies an agent channel token against the workflow's signing key and checks that it names agentKey and the expected workflow. Exchanging a token mints a new capability from it, so unlike a send it must not act on claims it has not verified.

func (*ChannelTokens) UnsafeParseValidateAgent added in v19.4.0

func (c *ChannelTokens) UnsafeParseValidateAgent(token string, agentKey api.AgentKey) (*ChannelTokenClaims, error)

UnsafeParseValidateAgent parses an agent channel token and checks that it names agentKey, which the module API takes from the connection the send arrived on. That check is the whole of what stops one module spending another's token, so this is the only parse that supplies a subject.

It is unsafe in the sense the other Unsafe parses are: see UnsafeParseValidateWorkflow.

func (*ChannelTokens) UnsafeParseValidateWorkflow added in v19.4.0

func (c *ChannelTokens) UnsafeParseValidateWorkflow(token string) (*ChannelTokenClaims, error)

UnsafeParseValidateWorkflow parses a workflow channel token. It checks no subject, because such a token is about no principal and this endpoint has none to check against: the workflow token presented alongside is what authorizes the send.

Neither the signature nor the alg header is verified, so a token signed with alg "none" passes. The send endpoints cannot do better, because the per-workflow signing key lives in the workflow state. The token travels with the signal and is verified with ParseVerifyValidate at signal promotion, the only place it is trusted.

type Engine

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

func NewEngine

func NewEngine(opts *Options) (*Engine, error)

func (*Engine) CancelWorkflow

func (e *Engine) CancelWorkflow(ctx context.Context, workflowKey, workflowToken string) error

CancelWorkflow requests cancellation of the workflow addressed by workflowKey. workflowToken is that workflow's token, or empty to present none. Cancellation is an asynchronous best-effort signal: the workflow transitions to a canceled state on a later replay round. Returns ErrInvalidWorkflowKey if the key is malformed, ErrWorkflowNotFound if no such workflow exists, or ErrInvalidWorkflowToken if the token does not verify.

func (*Engine) GetWorkflow

func (e *Engine) GetWorkflow(ctx context.Context, workflowKey, workflowToken string) (*Workflow, error)

GetWorkflow returns a snapshot of the workflow addressed by workflowKey, the key string returned by RunWorkflow. workflowToken is that workflow's token. Returns ErrInvalidWorkflowToken if the token does not verify, or an error if the key is malformed.

This is the polled endpoint, so the workflow and the metadata its token is verified against come from one read of one row. Nothing is returned before the token verifies; a workflow that is not there yields the same refusal as a wrong token, since a caller that cannot verify must not learn whether it exists.

func (*Engine) RunWorkflow

func (e *Engine) RunWorkflow(ctx context.Context, namespaceID int64, idempotencyKey string, opts *RunWorkflowOptions) (*RunWorkflowResult, error)

RunWorkflow submits a RunWorkflow workflow and mints its workflow token plus a channel token for every distinct channel the arguments carry. Returns the autocore-assigned workflow key.

If a workflow with the given (namespaceID, idempotencyKey) already exists, it returns that workflow's key without re-creating it, with tokens signed by the secret that workflow was created with. Those tokens are only handed over if opts.TokenBinding is the binding that workflow registered, so holding an idempotency key is not holding the workflow.

Returns ErrInvalidChannelName for a channel the caller may not name, ErrReservedIdempotencyKeyPrefix for an idempotency key the caller may not use, ErrMetadataTooLarge if opts.Annotations do not fit the workflow's metadata, ErrInputTooLarge if opts.WorkflowDefinition, opts.Args and opts.Kwargs together do not fit the workflow input, ErrInvalidTokenBinding if the existing workflow registered a different binding, ErrWorkflowNotFound if it is gone by the time its secret is read back, ErrTokenExpiryPassed if that workflow's recorded token expiry has passed, and ErrMissingSigningSecret if it holds no secret to sign with.

func (*Engine) SendToChannel added in v19.2.0

func (e *Engine) SendToChannel(ctx context.Context, agentKey api.AgentKey, channelToken string, sends []ChannelSend) error

SendToChannel puts each of sends onto the channel the module token addresses. Every send gets its own signal, keyed by its own idempotency key, so resending a batch lands nothing that already arrived.

func (*Engine) SendToWorkflowChannel added in v19.3.0

func (e *Engine) SendToWorkflowChannel(ctx context.Context, idempotencyKey, channelToken, workflowToken string, value *pkg_autoflow.Value) error

SendToWorkflowChannel sends value into the channel the caller token addresses. The token is the capability handed out by RunWorkflow for a channel in the workflow input. workflowToken is the token of the workflow that channel belongs to, or empty to present none.

type InvokeActionActivityInput

type InvokeActionActivityInput struct {
	AgentId       int64               `protobuf:"varint,1,opt,name=agent_id,json=agentId" json:"agent_id,omitempty"`
	AgentType     uint32              `protobuf:"varint,2,opt,name=agent_type,json=agentType" json:"agent_type,omitempty"`
	ModuleName    string              `protobuf:"bytes,3,opt,name=module_name,json=moduleName" json:"module_name,omitempty"`
	ActionName    string              `protobuf:"bytes,4,opt,name=action_name,json=actionName" json:"action_name,omitempty"`
	Args          []*v19.Value        `protobuf:"bytes,5,rep,name=args" json:"args,omitempty"`
	Kwargs        []*v19.Kwarg        `protobuf:"bytes,6,rep,name=kwargs" json:"kwargs,omitempty"`
	ChannelTokens []*v19.ChannelToken `protobuf:"bytes,7,rep,name=channel_tokens,json=channelTokens" json:"channel_tokens,omitempty"`
	Transforms    []*Transform        `protobuf:"bytes,8,rep,name=transforms" json:"transforms,omitempty"`
	Annotations   []*v19.Annotation   `protobuf:"bytes,9,rep,name=annotations" json:"annotations,omitempty"`
	// contains filtered or unexported fields
}

func (*InvokeActionActivityInput) Descriptor deprecated

func (*InvokeActionActivityInput) Descriptor() ([]byte, []int)

Deprecated: Use InvokeActionActivityInput.ProtoReflect.Descriptor instead.

func (*InvokeActionActivityInput) GetActionName

func (x *InvokeActionActivityInput) GetActionName() string

func (*InvokeActionActivityInput) GetAgentId added in v19.2.0

func (x *InvokeActionActivityInput) GetAgentId() int64

func (*InvokeActionActivityInput) GetAgentType added in v19.2.0

func (x *InvokeActionActivityInput) GetAgentType() uint32

func (*InvokeActionActivityInput) GetAnnotations added in v19.4.0

func (x *InvokeActionActivityInput) GetAnnotations() []*v19.Annotation

func (*InvokeActionActivityInput) GetArgs

func (x *InvokeActionActivityInput) GetArgs() []*v19.Value

func (*InvokeActionActivityInput) GetChannelTokens added in v19.2.0

func (x *InvokeActionActivityInput) GetChannelTokens() []*v19.ChannelToken

func (*InvokeActionActivityInput) GetKwargs

func (x *InvokeActionActivityInput) GetKwargs() []*v19.Kwarg

func (*InvokeActionActivityInput) GetModuleName

func (x *InvokeActionActivityInput) GetModuleName() string

func (*InvokeActionActivityInput) GetTransforms added in v19.3.0

func (x *InvokeActionActivityInput) GetTransforms() []*Transform

func (*InvokeActionActivityInput) ProtoMessage

func (*InvokeActionActivityInput) ProtoMessage()

func (*InvokeActionActivityInput) ProtoReflect

func (*InvokeActionActivityInput) Reset

func (x *InvokeActionActivityInput) Reset()

func (*InvokeActionActivityInput) String

func (x *InvokeActionActivityInput) String() string

type InvokeActionActivityOutput

type InvokeActionActivityOutput struct {
	Result *v19.Result `protobuf:"bytes,1,opt,name=result" json:"result,omitempty"`
	// contains filtered or unexported fields
}

func (*InvokeActionActivityOutput) Descriptor deprecated

func (*InvokeActionActivityOutput) Descriptor() ([]byte, []int)

Deprecated: Use InvokeActionActivityOutput.ProtoReflect.Descriptor instead.

func (*InvokeActionActivityOutput) GetResult

func (x *InvokeActionActivityOutput) GetResult() *v19.Result

func (*InvokeActionActivityOutput) ProtoMessage

func (*InvokeActionActivityOutput) ProtoMessage()

func (*InvokeActionActivityOutput) ProtoReflect

func (*InvokeActionActivityOutput) Reset

func (x *InvokeActionActivityOutput) Reset()

func (*InvokeActionActivityOutput) String

func (x *InvokeActionActivityOutput) String() string

type LoadModulesActivityInput added in v19.3.0

type LoadModulesActivityInput struct {
	WorkflowDefinition []byte `protobuf:"bytes,1,opt,name=workflow_definition,json=workflowDefinition" json:"workflow_definition,omitempty"`
	// contains filtered or unexported fields
}

func (*LoadModulesActivityInput) Descriptor deprecated added in v19.3.0

func (*LoadModulesActivityInput) Descriptor() ([]byte, []int)

Deprecated: Use LoadModulesActivityInput.ProtoReflect.Descriptor instead.

func (*LoadModulesActivityInput) GetWorkflowDefinition added in v19.3.0

func (x *LoadModulesActivityInput) GetWorkflowDefinition() []byte

func (*LoadModulesActivityInput) ProtoMessage added in v19.3.0

func (*LoadModulesActivityInput) ProtoMessage()

func (*LoadModulesActivityInput) ProtoReflect added in v19.3.0

func (x *LoadModulesActivityInput) ProtoReflect() protoreflect.Message

func (*LoadModulesActivityInput) Reset added in v19.3.0

func (x *LoadModulesActivityInput) Reset()

func (*LoadModulesActivityInput) String added in v19.3.0

func (x *LoadModulesActivityInput) String() string

type LoadModulesActivityOutput added in v19.3.0

type LoadModulesActivityOutput struct {
	Modules []*LoadedModule `protobuf:"bytes,1,rep,name=modules" json:"modules,omitempty"`
	// contains filtered or unexported fields
}

func (*LoadModulesActivityOutput) Descriptor deprecated added in v19.3.0

func (*LoadModulesActivityOutput) Descriptor() ([]byte, []int)

Deprecated: Use LoadModulesActivityOutput.ProtoReflect.Descriptor instead.

func (*LoadModulesActivityOutput) GetModules added in v19.3.0

func (x *LoadModulesActivityOutput) GetModules() []*LoadedModule

func (*LoadModulesActivityOutput) ProtoMessage added in v19.3.0

func (*LoadModulesActivityOutput) ProtoMessage()

func (*LoadModulesActivityOutput) ProtoReflect added in v19.3.0

func (*LoadModulesActivityOutput) Reset added in v19.3.0

func (x *LoadModulesActivityOutput) Reset()

func (*LoadModulesActivityOutput) String added in v19.3.0

func (x *LoadModulesActivityOutput) String() string

type LoadedModule added in v19.3.0

type LoadedModule struct {
	ModulePath           string                      `protobuf:"bytes,1,opt,name=module_path,json=modulePath" json:"module_path,omitempty"`
	ModuleName           string                      `protobuf:"bytes,2,opt,name=module_name,json=moduleName" json:"module_name,omitempty"`
	RegisteredModuleDesc *RegisteredModuleDescriptor `protobuf:"bytes,3,opt,name=registered_module_desc,json=registeredModuleDesc" json:"registered_module_desc,omitempty"`
	// contains filtered or unexported fields
}

func (*LoadedModule) Descriptor deprecated added in v19.3.0

func (*LoadedModule) Descriptor() ([]byte, []int)

Deprecated: Use LoadedModule.ProtoReflect.Descriptor instead.

func (*LoadedModule) GetModuleName added in v19.3.0

func (x *LoadedModule) GetModuleName() string

func (*LoadedModule) GetModulePath added in v19.3.0

func (x *LoadedModule) GetModulePath() string

func (*LoadedModule) GetRegisteredModuleDesc added in v19.3.0

func (x *LoadedModule) GetRegisteredModuleDesc() *RegisteredModuleDescriptor

func (*LoadedModule) ProtoMessage added in v19.3.0

func (*LoadedModule) ProtoMessage()

func (*LoadedModule) ProtoReflect added in v19.3.0

func (x *LoadedModule) ProtoReflect() protoreflect.Message

func (*LoadedModule) Reset added in v19.3.0

func (x *LoadedModule) Reset()

func (*LoadedModule) String added in v19.3.0

func (x *LoadedModule) String() string

type ModuleDescriptorResolver

type ModuleDescriptorResolver struct {
	Desc *pkg_autoflow.ModuleDescriptor
	// ModuleURI is the normalized URI the module was loaded from. A module file
	// runs under it rather than under its bare file name, so two modules shipping
	// an identically named file stay distinguishable in error messages and
	// wherever a symbol is attributed to the file that defined it.
	ModuleURI        string
	PredeclaredFiles *protoregistry.Files
	Backend          autoflow.Backend
	Loader           autoflow.Loader
	Tracer           trace.Tracer
	// NewAction builds the action for each of the module's actions.
	NewAction NewActionFunc
	// OnThreadEnd, when set, is called once a module file's thread finished.
	OnThreadEnd autoflow.ThreadEndFunc
}

func (*ModuleDescriptorResolver) LoadFile added in v19.3.0

func (r *ModuleDescriptorResolver) LoadFile(ctx context.Context, fileName string) (starlark.StringDict, error)

func (*ModuleDescriptorResolver) LoadSymbols

func (r *ModuleDescriptorResolver) LoadSymbols() (starlark.StringDict, error)

type ModuleManager

type ModuleManager interface {
	// LoadModuleDescriptor loads a registered module's descriptor.
	// Returns (nil, nil) if not found.
	//
	// Both a nil descriptor and an error are taken to be transient and are
	// retried, because a descriptor is briefly absent whenever the module
	// re-registers. An implementation whose refusal no repeat can change reports
	// it with NonRetryable.
	LoadModuleDescriptor(ctx context.Context, moduleName string) (*RegisteredModuleDescriptor, error)
	InvokeAction(context.Context, api.AgentKey, *pkg_autoflow.ActionInvocation) (*pkg_autoflow.Result, error)
}

type ModuleURI added in v19.3.0

type ModuleURI struct {
	api.AutoFlowModuleKey
	File string
}

ModuleURI represents an AutoFlow module URI.

It has the following structure: "module:[module/path/]<module_name>[?params]" Module path and parameters are optional. Parameters are represented as usual URL query parameters.

The supported parameters are: - file: name of a file, exposed by a module.

Module names are agent names, defined in doc/identity_and_auth.md.

func ParseModuleURI added in v19.3.0

func ParseModuleURI(u string) (*ModuleURI, error)

func (*ModuleURI) String added in v19.3.0

func (u *ModuleURI) String() string

type NewActionFunc added in v19.3.0

type NewActionFunc func(action *pkg_autoflow.Action, conv *ValueConverter) autoflow.Action

NewActionFunc builds the Action for one of a module's actions.

type Options

type Options struct {
	Log            *slog.Logger
	Client         autocore.Client
	Registry       *autocore.Registry
	ModuleManager  ModuleManager
	MeterProvider  metric.MeterProvider
	TracerProvider trace.TracerProvider
	Validator      protovalidate.Validator
	// PredeclaredProtoFiles is the set of proto file descriptors AutoFlow modules'
	// descriptor sets may import, used to resolve imports when reconstructing
	// module proto descriptors. There is deliberately no fallback to
	// protoregistry.GlobalFiles, so a module cannot pull in arbitrary types linked
	// into the binary. Ordered: a file can only import files before it.
	PredeclaredProtoFiles []protoreflect.FileDescriptor
	TokenSigningMethod    *jwt.SigningMethodHMAC
	// ChannelTokens is built by the caller because the built-in modules that use it
	// are constructed before the engine. Engine holds that same instance, so a token
	// is minted and parsed with one configuration wherever it is handled.
	ChannelTokens *ChannelTokens
}

type RegisteredModuleDescriptor added in v19.2.0

type RegisteredModuleDescriptor struct {
	ModuleDesc *v19.ModuleDescriptor `protobuf:"bytes,1,opt,name=module_desc,json=moduleDesc" json:"module_desc,omitempty"`
	AgentId    int64                 `protobuf:"varint,2,opt,name=agent_id,json=agentId" json:"agent_id,omitempty"`
	AgentType  uint32                `protobuf:"varint,3,opt,name=agent_type,json=agentType" json:"agent_type,omitempty"`
	// contains filtered or unexported fields
}

func (*RegisteredModuleDescriptor) Descriptor deprecated added in v19.2.0

func (*RegisteredModuleDescriptor) Descriptor() ([]byte, []int)

Deprecated: Use RegisteredModuleDescriptor.ProtoReflect.Descriptor instead.

func (*RegisteredModuleDescriptor) GetAgentId added in v19.2.0

func (x *RegisteredModuleDescriptor) GetAgentId() int64

func (*RegisteredModuleDescriptor) GetAgentType added in v19.2.0

func (x *RegisteredModuleDescriptor) GetAgentType() uint32

func (*RegisteredModuleDescriptor) GetModuleDesc added in v19.2.0

func (x *RegisteredModuleDescriptor) GetModuleDesc() *v19.ModuleDescriptor

func (*RegisteredModuleDescriptor) ProtoMessage added in v19.2.0

func (*RegisteredModuleDescriptor) ProtoMessage()

func (*RegisteredModuleDescriptor) ProtoReflect added in v19.2.0

func (*RegisteredModuleDescriptor) Reset added in v19.2.0

func (x *RegisteredModuleDescriptor) Reset()

func (*RegisteredModuleDescriptor) String added in v19.2.0

func (x *RegisteredModuleDescriptor) String() string

type RunWorkflowInput

type RunWorkflowInput struct {
	WorkflowDefinition []byte       `protobuf:"bytes,1,opt,name=workflow_definition,json=workflowDefinition" json:"workflow_definition,omitempty"`
	Args               []*v19.Value `protobuf:"bytes,2,rep,name=args" json:"args,omitempty"`
	Kwargs             []*v19.Kwarg `protobuf:"bytes,3,rep,name=kwargs" json:"kwargs,omitempty"`
	EntryFunction      string       `protobuf:"bytes,6,opt,name=entry_function,json=entryFunction" json:"entry_function,omitempty"`
	// contains filtered or unexported fields
}

func (*RunWorkflowInput) Descriptor deprecated

func (*RunWorkflowInput) Descriptor() ([]byte, []int)

Deprecated: Use RunWorkflowInput.ProtoReflect.Descriptor instead.

func (*RunWorkflowInput) GetArgs added in v19.2.0

func (x *RunWorkflowInput) GetArgs() []*v19.Value

func (*RunWorkflowInput) GetEntryFunction added in v19.3.0

func (x *RunWorkflowInput) GetEntryFunction() string

func (*RunWorkflowInput) GetKwargs added in v19.2.0

func (x *RunWorkflowInput) GetKwargs() []*v19.Kwarg

func (*RunWorkflowInput) GetWorkflowDefinition

func (x *RunWorkflowInput) GetWorkflowDefinition() []byte

func (*RunWorkflowInput) ProtoMessage

func (*RunWorkflowInput) ProtoMessage()

func (*RunWorkflowInput) ProtoReflect

func (x *RunWorkflowInput) ProtoReflect() protoreflect.Message

func (*RunWorkflowInput) Reset

func (x *RunWorkflowInput) Reset()

func (*RunWorkflowInput) String

func (x *RunWorkflowInput) String() string

type RunWorkflowOptions added in v19.3.0

type RunWorkflowOptions struct {
	WorkflowDefinition        []byte
	Args                      []*pkg_autoflow.Value
	Kwargs                    []*pkg_autoflow.Kwarg
	ScheduleToCompleteTimeout time.Duration
	// TokenBinding is the value a re-submission must present to be handed this
	// workflow's tokens again. Required of every caller; empty registers none,
	// which leaves the workflow's tokens un-re-issuable for good.
	TokenBinding []byte
	// Annotations are the caller's tags on this execution, inherited by every child
	// the workflow spawns.
	Annotations []*pkg_autoflow.Annotation
}

RunWorkflowOptions is what a caller passes to RunWorkflow. It is deliberately not RunWorkflowInput: that message is the workflow's own input and carries fields only flowcore may set, such as the signing secret and the entry function a parent picks for a child.

type RunWorkflowOutput

type RunWorkflowOutput struct {
	Result *v19.Value `protobuf:"bytes,1,opt,name=result" json:"result,omitempty"`
	// contains filtered or unexported fields
}

func (*RunWorkflowOutput) Descriptor deprecated

func (*RunWorkflowOutput) Descriptor() ([]byte, []int)

Deprecated: Use RunWorkflowOutput.ProtoReflect.Descriptor instead.

func (*RunWorkflowOutput) GetResult added in v19.2.0

func (x *RunWorkflowOutput) GetResult() *v19.Value

func (*RunWorkflowOutput) ProtoMessage

func (*RunWorkflowOutput) ProtoMessage()

func (*RunWorkflowOutput) ProtoReflect

func (x *RunWorkflowOutput) ProtoReflect() protoreflect.Message

func (*RunWorkflowOutput) Reset

func (x *RunWorkflowOutput) Reset()

func (*RunWorkflowOutput) String

func (x *RunWorkflowOutput) String() string

type RunWorkflowResult added in v19.3.0

type RunWorkflowResult struct {
	WorkflowKey   string
	WorkflowToken string
	ChannelTokens []*pkg_autoflow.ChannelToken
}

RunWorkflowResult is what RunWorkflow returns. WorkflowToken is the capability to manage the workflow. ChannelTokens holds one send-only capability per distinct channel in the workflow input, in first-encounter order, and is empty when the input carries no channel.

type Transform added in v19.3.0

type Transform struct {
	Source       []byte `protobuf:"bytes,1,opt,name=source" json:"source,omitempty"`
	FunctionName string `protobuf:"bytes,2,opt,name=function_name,json=functionName" json:"function_name,omitempty"`
	FileName     string `protobuf:"bytes,3,opt,name=file_name,json=fileName" json:"file_name,omitempty"`
	// contains filtered or unexported fields
}

func (*Transform) Descriptor deprecated added in v19.3.0

func (*Transform) Descriptor() ([]byte, []int)

Deprecated: Use Transform.ProtoReflect.Descriptor instead.

func (*Transform) GetFileName added in v19.3.0

func (x *Transform) GetFileName() string

func (*Transform) GetFunctionName added in v19.3.0

func (x *Transform) GetFunctionName() string

func (*Transform) GetSource added in v19.3.0

func (x *Transform) GetSource() []byte

func (*Transform) ProtoMessage added in v19.3.0

func (*Transform) ProtoMessage()

func (*Transform) ProtoReflect added in v19.3.0

func (x *Transform) ProtoReflect() protoreflect.Message

func (*Transform) Reset added in v19.3.0

func (x *Transform) Reset()

func (*Transform) String added in v19.3.0

func (x *Transform) String() string

type ValidatingChannelTokenClaims added in v19.2.0

type ValidatingChannelTokenClaims struct {
	ChannelTokenClaims

	Validator protovalidate.Validator
	// ExpectedSubject is the principal the token must be about, or empty to check
	// none. Only the module API knows a principal to check against; every other
	// parse leaves this empty and relies on the audience to keep the kinds apart.
	ExpectedSubject     string
	ExpectedChannelName string
	ExpectedShardID     autocore.ShardID
	ExpectedWorkflowID  autocore.WorkflowID
}

func (*ValidatingChannelTokenClaims) Validate added in v19.2.0

func (v *ValidatingChannelTokenClaims) Validate() error

type ValidatingWorkflowTokenClaims added in v19.4.0

type ValidatingWorkflowTokenClaims struct {
	WorkflowTokenClaims

	Validator          protovalidate.Validator
	ExpectedShardID    autocore.ShardID
	ExpectedWorkflowID autocore.WorkflowID
}

func (*ValidatingWorkflowTokenClaims) Validate added in v19.4.0

func (v *ValidatingWorkflowTokenClaims) Validate() error

type ValueConverter

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

ValueConverter converts between AutoFlow proto Values and Starlark values.

Proto message Values (google.protobuf.Any) are resolved against the supplied descriptor set, falling back to the predeclared files. protoregistry.GlobalFiles is intentionally NOT consulted. Safe to use concurrently. A converter with a channel resolver installed is the exception: the resolver reaches into the running workflow's state, so that converter is confined to the workflow goroutine.

func NewValueConverter

func NewValueConverter(predeclaredFiles *protoregistry.Files, descriptorSet *descriptorpb.FileDescriptorSet) *ValueConverter

NewValueConverter constructs a new ValueConverter. descriptorSet is optional, can be nil

func (*ValueConverter) FromStarlark

func (c *ValueConverter) FromStarlark(v starlark.Value) (*pkg_autoflow.Value, error)

FromStarlark converts a Starlark value into a proto Value.

func (*ValueConverter) FromStarlarkIterable added in v19.3.0

func (c *ValueConverter) FromStarlarkIterable(it iterable) ([]*pkg_autoflow.Value, error)

func (*ValueConverter) FromStarlarkKwargs added in v19.3.0

func (c *ValueConverter) FromStarlarkKwargs(kwargs []starlark.Tuple) ([]*pkg_autoflow.Kwarg, error)

FromStarlarkKwargs converts Starlark keyword arguments to proto Kwargs.

func (*ValueConverter) ToStarlark

func (c *ValueConverter) ToStarlark(v *pkg_autoflow.Value) (starlark.Value, error)

ToStarlark converts a proto Value into a Starlark value.

type ValueToStarlarkConverter added in v19.2.0

type ValueToStarlarkConverter interface {
	ToStarlark(*pkg_autoflow.Value) (starlark.Value, error)
}

ValueToStarlarkConverter converts an AutoFlow proto Value into a Starlark value.

type Workflow

type Workflow struct {
	Key            string
	Name           string
	IdempotencyKey string
	State          WorkflowState
	CreatedAt      time.Time
	UpdatedAt      time.Time
	Output         *RunWorkflowOutput
	Error          string
}

Workflow is a read-only snapshot of a workflow returned by GetWorkflow. Output holds the workflow's decoded output and is set only when the workflow completed; Error holds the failure message and is set only when it failed.

type WorkflowMetadata added in v19.3.0

type WorkflowMetadata struct {
	SigningSecret    []byte                 `protobuf:"bytes,1,opt,name=signing_secret,json=signingSecret" json:"signing_secret,omitempty"`
	TokenBindingHash []byte                 `protobuf:"bytes,2,opt,name=token_binding_hash,json=tokenBindingHash" json:"token_binding_hash,omitempty"`
	TokenExpiresAt   *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=token_expires_at,json=tokenExpiresAt" json:"token_expires_at,omitempty"`
	Annotations      []*v19.Annotation      `protobuf:"bytes,4,rep,name=annotations" json:"annotations,omitempty"`
	// contains filtered or unexported fields
}

func (*WorkflowMetadata) Descriptor deprecated added in v19.3.0

func (*WorkflowMetadata) Descriptor() ([]byte, []int)

Deprecated: Use WorkflowMetadata.ProtoReflect.Descriptor instead.

func (*WorkflowMetadata) GetAnnotations added in v19.4.0

func (x *WorkflowMetadata) GetAnnotations() []*v19.Annotation

func (*WorkflowMetadata) GetSigningSecret added in v19.4.0

func (x *WorkflowMetadata) GetSigningSecret() []byte

func (*WorkflowMetadata) GetTokenBindingHash added in v19.4.0

func (x *WorkflowMetadata) GetTokenBindingHash() []byte

func (*WorkflowMetadata) GetTokenExpiresAt added in v19.4.0

func (x *WorkflowMetadata) GetTokenExpiresAt() *timestamppb.Timestamp

func (*WorkflowMetadata) ProtoMessage added in v19.3.0

func (*WorkflowMetadata) ProtoMessage()

func (*WorkflowMetadata) ProtoReflect added in v19.3.0

func (x *WorkflowMetadata) ProtoReflect() protoreflect.Message

func (*WorkflowMetadata) Reset added in v19.3.0

func (x *WorkflowMetadata) Reset()

func (*WorkflowMetadata) String added in v19.3.0

func (x *WorkflowMetadata) String() string

type WorkflowState

type WorkflowState = autocore.WorkflowState

WorkflowState is the lifecycle state of an AutoFlow workflow.

type WorkflowTokenClaims added in v19.4.0

type WorkflowTokenClaims struct {
	RegisteredClaimIssuer    string   `protobuf:"bytes,1,opt,name=registered_claim_issuer,json=iss" json:"registered_claim_issuer,omitempty"`
	RegisteredClaimAudience  []string `protobuf:"bytes,2,rep,name=registered_claim_audience,json=aud" json:"registered_claim_audience,omitempty"`
	RegisteredClaimExpiresAt float64  `protobuf:"fixed64,3,opt,name=registered_claim_expires_at,json=exp" json:"registered_claim_expires_at,omitempty"`
	RegisteredClaimNotBefore float64  `protobuf:"fixed64,4,opt,name=registered_claim_not_before,json=nbf" json:"registered_claim_not_before,omitempty"`
	RegisteredClaimIssuedAt  float64  `protobuf:"fixed64,5,opt,name=registered_claim_issued_at,json=iat" json:"registered_claim_issued_at,omitempty"`
	ShardId                  int32    `protobuf:"varint,6,opt,name=shard_id" json:"shard_id,omitempty"`
	WorkflowId               string   `protobuf:"bytes,7,opt,name=workflow_id,json=wf_id" json:"workflow_id,omitempty"`
	// contains filtered or unexported fields
}

func (*WorkflowTokenClaims) Descriptor deprecated added in v19.4.0

func (*WorkflowTokenClaims) Descriptor() ([]byte, []int)

Deprecated: Use WorkflowTokenClaims.ProtoReflect.Descriptor instead.

func (*WorkflowTokenClaims) GetAudience added in v19.4.0

func (c *WorkflowTokenClaims) GetAudience() (jwt.ClaimStrings, error)

func (*WorkflowTokenClaims) GetExpirationTime added in v19.4.0

func (c *WorkflowTokenClaims) GetExpirationTime() (*jwt.NumericDate, error)

func (*WorkflowTokenClaims) GetIssuedAt added in v19.4.0

func (c *WorkflowTokenClaims) GetIssuedAt() (*jwt.NumericDate, error)

func (*WorkflowTokenClaims) GetIssuer added in v19.4.0

func (c *WorkflowTokenClaims) GetIssuer() (string, error)

func (*WorkflowTokenClaims) GetNotBefore added in v19.4.0

func (c *WorkflowTokenClaims) GetNotBefore() (*jwt.NumericDate, error)

func (*WorkflowTokenClaims) GetRegisteredClaimAudience added in v19.4.0

func (x *WorkflowTokenClaims) GetRegisteredClaimAudience() []string

func (*WorkflowTokenClaims) GetRegisteredClaimExpiresAt added in v19.4.0

func (x *WorkflowTokenClaims) GetRegisteredClaimExpiresAt() float64

func (*WorkflowTokenClaims) GetRegisteredClaimIssuedAt added in v19.4.0

func (x *WorkflowTokenClaims) GetRegisteredClaimIssuedAt() float64

func (*WorkflowTokenClaims) GetRegisteredClaimIssuer added in v19.4.0

func (x *WorkflowTokenClaims) GetRegisteredClaimIssuer() string

func (*WorkflowTokenClaims) GetRegisteredClaimNotBefore added in v19.4.0

func (x *WorkflowTokenClaims) GetRegisteredClaimNotBefore() float64

func (*WorkflowTokenClaims) GetShardId added in v19.4.0

func (x *WorkflowTokenClaims) GetShardId() int32

func (*WorkflowTokenClaims) GetSubject added in v19.4.0

func (c *WorkflowTokenClaims) GetSubject() (string, error)

func (*WorkflowTokenClaims) GetWorkflowId added in v19.4.0

func (x *WorkflowTokenClaims) GetWorkflowId() string

func (*WorkflowTokenClaims) MarshalJSON added in v19.4.0

func (c *WorkflowTokenClaims) MarshalJSON() ([]byte, error)

func (*WorkflowTokenClaims) ProtoMessage added in v19.4.0

func (*WorkflowTokenClaims) ProtoMessage()

func (*WorkflowTokenClaims) ProtoReflect added in v19.4.0

func (x *WorkflowTokenClaims) ProtoReflect() protoreflect.Message

func (*WorkflowTokenClaims) Reset added in v19.4.0

func (x *WorkflowTokenClaims) Reset()

func (*WorkflowTokenClaims) String added in v19.4.0

func (x *WorkflowTokenClaims) String() string

func (*WorkflowTokenClaims) UnmarshalJSON added in v19.4.0

func (c *WorkflowTokenClaims) UnmarshalJSON(b []byte) error

func (*WorkflowTokenClaims) WorkflowKey added in v19.4.0

func (c *WorkflowTokenClaims) WorkflowKey() (autocore.WorkflowKey, error)

Jump to

Keyboard shortcuts

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