Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrUnknownKind = errors.New("no such connection kind") ErrUnknownAction = errors.New("no such action") ErrNeedApproval = errors.New("this action leaves the organisation and needs a human approval") ErrNeedIdem = errors.New("a live send needs an idempotency key") ErrBadPrincipal = errors.New("the acting principal is not one that can be audited") ErrNotConfigured = errors.New("this connection is not configured") ErrBlockedSF001 = errors.New("connections cannot read their credential yet — see SF-001 in docs/security-findings.md") )
Errors the runner and the API distinguish between.
var ErrAlreadySent = errors.New("this idempotency key has already been used; not sending again")
ErrAlreadySent is returned when an idempotency key has been used before. Not an error condition so much as the guarantee working.
Functions ¶
func Kinds ¶
func Kinds() []string
Kinds lists every registered actor kind, sorted so the UI is stable.
Types ¶
type ActInput ¶
type ActInput struct {
Action string `json:"action"`
Params json.RawMessage `json:"params"`
Actor Principal `json:"actor"`
// Idem is mandatory unless DryRun. UNIQUE per effect.
Idem string `json:"idem"`
RunID string `json:"runId"`
ApprovalID string `json:"approvalId"`
// DryRun MUST NOT touch the network. Fill Preview and return.
DryRun bool `json:"dryRun"`
}
type ActOutput ¶
type ActOutput struct {
ProviderRef string `json:"providerRef"`
Preview string `json:"preview"`
Result json.RawMessage `json:"result"`
Retryable bool `json:"retryable"`
// RetryAfter is honoured by the CALLER. An actor never sleeps inside Act —
// a provider that says "wait 30s" must not hold a request goroutine for 30s.
RetryAfter time.Duration `json:"retryAfter"`
}
type ActionSpec ¶
type ActionSpec struct {
Name string `json:"name"`
Title Text `json:"title"`
Description Text `json:"description"`
InputSchema json.RawMessage `json:"inputSchema"`
// External means it leaves the organisation's boundary. Rule 41: an agent
// may draft it, only a human may send it. Forces approval — see Run.
External bool `json:"external"`
// Idempotent means the same Idem key may be retried safely. Default false,
// and nothing else is ever auto-retried (Rule 39): "did the first call
// land?" must be answerable before anything sends twice.
Idempotent bool `json:"idempotent"`
Destructive bool `json:"destructive"`
// Grant is an exact permission string. togo's Can() is an exact match, so
// "*" grants nothing — see Rule 16.
Grant string `json:"grant"`
RatePerMin int `json:"ratePerMin"`
}
ActionSpec describes one thing an actor can do.
One schema, three consumers: the create form renders it, the admin panel turns it into a tool definition, and the runner validates against it. Three copies of the same shape is how they drift.
func SpecFor ¶
func SpecFor(kind, action string) (ActionSpec, bool)
SpecFor finds one action's spec.
type Actor ¶
type Actor interface {
// Kind is the registry key: "slack", "discord", "telegram", "email", "webhook".
Kind() string
// Actions is the full set this actor supports.
Actions() []ActionSpec
// Act performs one. It must honour DryRun by rendering Preview and
// returning without touching the network.
Act(ctx context.Context, in ActInput, cfg json.RawMessage, sec Secrets) (ActOutput, error)
}
Actor is a connection that sends.
type Blocked ¶
type Blocked struct {
// Kind and Name name the connection, so the error says which one.
Kind string
Name string
}
Blocked is a Secrets that refuses, with the reason.
It is the default wiring, so an operator who configures a connection before SF-001 is resolved gets a precise message instead of a puzzling auth failure from Slack.
type Principal ¶
type Principal struct {
Kind string `json:"kind"` // user | agent | connection | system
Slug string `json:"slug"`
ID string `json:"id"`
}
Principal is WHO asked. A connector never picks its own.
type Runner ¶
type Runner struct {
Store Store
Log *slog.Logger
// Secrets resolves a connection's credential. Blocked{} until SF-001 is
// decided — see secrets.go.
Secrets func(kind, name string) Secrets
}
Runner performs actions. One per process.
type Secrets ¶
Secrets is how an actor reads its credential. One method, so the blocked seam is one function (secrets.go) rather than a vault import in five files.
type Static ¶
Static is a Secrets backed by an in-memory map.
For TESTS and for DryRun paths only. It is exported because the actor tests need to render a payload without a vault, and giving them a fake here is better than each one inventing its own — but it must never be wired into a live send: a credential in a Go map is a credential in a core dump, a heap snapshot, and every backup of the process that held it (Rule 34).
type Store ¶
type Store interface {
// Claim inserts the outbox row, or returns the existing one for this
// idempotency key. `fresh` is false when the key was already present.
Claim(ctx context.Context, row OutboxRow) (id string, fresh bool, err error)
// Finish records the terminal state.
Finish(ctx context.Context, id string, status string, out ActOutput, sendErr error) error
}
Store is the persistence an actor run needs. An interface so the tests can exercise the ordering without a database.