Documentation
¶
Overview ¶
Package wasmloop is the extension tier: third-party loops as signed WASM modules instead of Go compiled into the daemon.
What it replaces: `karmax loops install` ran `go get`, rewrote a generated file, rebuilt the binary and swapped it. That needs a Go toolchain and a source checkout on every user's machine, and it compiles unreviewed third-party Go into a process holding WhatsApp sessions, Google tokens and a GitLoom API key. There is no boundary in that design to enforce.
Install here is a TRANSACTION — fetch, verify, record, activate — and raw bytes are never executed on arrival.
Index ¶
- Constants
- func Describe(m Manifest) []string
- func Dir() string
- func LockPath(dir string) string
- func Pack(m Manifest, module []byte, sigs []Signature) ([]byte, error)
- func SaveTrust(dir string, registries, revoked []string, allowCommunity bool) error
- func StoredTrust(dir string) (registries, revoked []string, allowCommunity bool)
- func TrustPath(dir string) string
- func UnknownHostFunctions(declared []string) []string
- func VerifyBytes(data []byte, t Trust) (*Artifact, *Verdict, error)
- type Artifact
- type CapabilityDiff
- type ChatSummary
- type Client
- type Entry
- type Index
- type Installer
- type Kind
- type Kit
- type Lock
- type Manifest
- type Options
- type Preview
- type RegistryEntry
- type Role
- type Runner
- func (r *Runner) Close(ctx context.Context) error
- func (r *Runner) InvokeTool(ctx context.Context, name string, input map[string]any, timeout time.Duration) (string, error)
- func (r *Runner) ProvidedTools() []ToolSpec
- func (r *Runner) Run(ctx context.Context, timeout time.Duration) error
- func (r *Runner) RunTriggered(ctx context.Context, timeout time.Duration, kind string, ...) error
- type ShortMemory
- type Signature
- type Store
- type Tier
- type ToolSpec
- type Trust
- type Verdict
Constants ¶
const ( FnLog = "log" FnRecall = "recall" FnRemember = "remember" FnNotify = "notify" FnHTTP = "http" FnTrigger = "trigger" FnAsk = "ask" FnConfig = "config" FnHostTool = "hosttool" FnHarness = "harness" FnGateway = "gateway" FnSummarize = "summarize" FnPropose = "propose" FnRemind = "remind" FnTool = "tool" FnShortSet = "short_set" FnShortGet = "short_get" FnShortAll = "short_all" FnChatGet = "chat_summary_get" FnChatSave = "chat_summary_save" FnRunLoop = "run_loop" FnShortForget = "short_forget" FnOperators = "operator_chats" )
Host function names. A closed set: an unknown name is refused, not ignored, so a module built against a newer KARMAX fails visibly here.
const AllocExport = "karmax_alloc"
AllocExport is the guest's allocator, used to hand it a request.
const DefaultRegistry = "https://raw.githubusercontent.com/MelloB1989/karmax-loops/main"
DefaultRegistry is where loops are fetched from.
const IndexFile = "/index.json"
IndexFile is the index's path within a registry.
const ToolExport = "tool"
ToolExport is the function a module exports to serve its provided tools.
Variables ¶
This section is empty.
Functions ¶
func Describe ¶
Describe renders a manifest in the plain English an operator approves.
It takes the whole manifest rather than the fields it happens to use today, so a new kind of reach cannot be added to the format and quietly stay out of the list the operator is shown.
func SaveTrust ¶
SaveTrust writes the configuration, without the environment's additions — what the operator set is what is stored.
func StoredTrust ¶
StoredTrust reads only the file, for a command that is editing it.
func UnknownHostFunctions ¶
UnknownHostFunctions reports which of a manifest's declared host functions this KARMAX does not have.
An artifact's signature proves it is intact, not that it still fits the host. A loop built against an older ABI verifies, installs and runs — and is refused on its first real call, which looks like a loop that decided to do nothing. This is what lets that be reported instead of discovered.
Types ¶
type Artifact ¶
Artifact is a parsed loop package.
type CapabilityDiff ¶
CapabilityDiff describes how an upgrade changes what a loop may do.
This is the screen that matters at upgrade time: a loop that gained "http:*" between versions is a loop the operator must be shown, not one that quietly inherits its old approval.
func DiffCapabilities ¶
func DiffCapabilities(old Entry, next Manifest) CapabilityDiff
DiffCapabilities compares an installed entry with an incoming manifest.
type ChatSummary ¶
type ChatSummary struct {
ChatJID string `json:"jid"`
ChatName string `json:"name"`
IsGroup bool `json:"is_group"`
Summary string `json:"summary"`
MessageCount int `json:"message_count"`
OwnMessageCount int `json:"own_count"`
// Unix seconds, because a guest and a host do not share a time.Time.
LastMessageAt int64 `json:"last_message_at"`
SummarizedAt int64 `json:"summarized_at"`
Status string `json:"status"`
}
ChatSummary is the stored cold-memory record for one chat.
type Client ¶
Client fetches from a registry.
func NewClient ¶
func NewClient() *Client
NewClient builds a registry client for the configured registry.
func (*Client) Fetch ¶
Fetch downloads an entry's artifact and checks it against the index.
The digest is checked HERE, before the bytes go anywhere near an installer. For a workflow the signature would catch a swap anyway; for a recipe there is no signature, so this is the only thing standing between a compromised CDN and an automation running on the operator's behalf.
type Entry ¶
type Entry struct {
Name string `json:"name"`
Version string `json:"version"`
SHA256 string `json:"sha256"`
Publisher string `json:"publisher"`
Registry string `json:"registry,omitempty"`
Tier Tier `json:"tier"`
Host []string `json:"host"`
Capabilities []string `json:"capabilities"`
Source string `json:"source,omitempty"`
File string `json:"file"`
InstalledBy string `json:"installed_by"`
InstalledAt time.Time `json:"installed_at"`
Enabled bool `json:"enabled"`
}
Entry is one installed loop.
type Index ¶
type Index struct {
Version int `json:"version"`
Entries []RegistryEntry `json:"entries"`
// RegistryKey is the countersigning key this registry uses. Published here
// so `karmax loops trust-registry` can offer it — an operator still has to
// accept it, since a key that vouches for itself vouches for nothing.
RegistryKey string `json:"registry_key,omitempty"`
}
Index is a registry's catalogue.
func (*Index) Find ¶
func (idx *Index) Find(name string) (RegistryEntry, bool)
Find returns one entry by name.
func (*Index) ShipWith ¶
func (idx *Index) ShipWith() []RegistryEntry
ShipWith returns the entries a fresh install starts with.
type Installer ¶
type Installer struct {
Dir string // ~/.karmax/loops
Broker Store
Trust Trust
// Actor is recorded in the lockfile, so "who installed this" has an answer.
Actor string
}
Installer performs installs against a directory.
func (*Installer) Inspect ¶
Inspect verifies an artifact and reports what installing it would mean, without writing anything.
func (*Installer) Install ¶
Install writes the artifact, records it, and grants exactly what its manifest declared.
Order matters: the file is written before the lockfile so a lockfile entry never names a file that is not there, and grants are replaced wholesale so an upgrade that dropped a capability actually loses it.
func (*Installer) Load ¶
Load reads an installed loop back, re-verifying it.
The digest is checked against the LOCKFILE, not just against the manifest inside the file — otherwise someone who replaced the artifact wholesale, with a matching self-consistent manifest, would pass. What was approved is what is recorded, and that is what has to still be true.
type Kit ¶
type Kit interface {
Recall(query string, limit int) ([]string, error)
Remember(fact string) error
Notify(title, body string) error
Ask(ctx context.Context, prompt string) (string, error)
HTTP(ctx context.Context, method, url string, headers map[string]string, body string) (string, int, error)
Config(key string) string
HostTool(name string) string
Harness(ctx context.Context, prompt string) (string, error)
Gateway(ctx context.Context, prompt string, lend ...string) (string, error)
Summarize(ctx context.Context, prompt string) (string, error)
Propose(title, summary, action string) error
Remind(title, due, notes string) error
// Tool calls one of KARMAX's tools by name. Integrations reach a loop
// through here and nowhere else, so adding one costs no ABI.
Tool(ctx context.Context, name string, input map[string]any) (string, error)
ShortSet(group, key, value string, ttlSeconds int) error
ShortGet(group, key string) (string, bool, error)
ShortAll(group string) ([]ShortMemory, error)
ChatSummary(jid string) (*ChatSummary, error)
SaveChatSummary(ChatSummary) error
RunLoop(name string) error
ShortForget(group, key string) error
OperatorChats() []string
}
Kit is what the host does on the guest's behalf. Injected so the runner has no opinion about where memory or HTTP actually live.
type Lock ¶
Lock is the whole file.
type Manifest ¶
type Manifest struct {
Name string `json:"name" yaml:"name"`
Version string `json:"version" yaml:"version"`
Description string `json:"description" yaml:"description"`
// Publisher is the Ed25519 key that signed this artifact.
Publisher string `json:"publisher" yaml:"publisher"`
// Host names the host functions the module may call. Anything not listed
// is refused at runtime, not logged.
Host []string `json:"host" yaml:"host"`
// Tools names the KARMAX tools the module may call through the `tool` host
// function. Every integration reaches a loop this way, so this list is the
// whole of what it can touch outside its own sandbox.
Tools []string `json:"tools,omitempty" yaml:"tools"`
// Provides are tools this module IMPLEMENTS and lends to the agent. They
// exist only while the agent is working on this loop's behalf.
Provides []ToolSpec `json:"provides,omitempty" yaml:"provides"`
// Capabilities are Broker grants in class:value form — "http:api.github.com",
// "memory:nexus:write", "tool:comms.send".
Capabilities []string `json:"capabilities" yaml:"capabilities"`
// Triggers.
Schedule string `json:"schedule,omitempty" yaml:"schedule"`
Events []string `json:"events,omitempty" yaml:"events"`
Webhook string `json:"webhook,omitempty" yaml:"webhook"`
// Limits the operator can see before installing.
MemoryMB int `json:"memory_mb,omitempty" yaml:"memory_mb"`
// SHA256 is the hex digest of the module bytes, inside the signature so the
// code cannot be swapped for different code under the same manifest.
SHA256 string `json:"sha256" yaml:"sha256"`
// Provenance.
SourceURL string `json:"source_url,omitempty" yaml:"source_url"`
BuiltAt int64 `json:"built_at,omitempty" yaml:"built_at"`
}
Manifest is what the artifact declares about itself. It is inside the signature, so none of it can be changed after publishing.
The yaml tags are explicit because the manifest is also hand-written as loop.yaml, and yaml.v3 does not read json tags — it lowercases field names instead. Without them `memory_mb: 32` parsed as nothing at all and every loop silently took the default limit.
func (*Manifest) BuiltAtTime ¶
BuiltAtTime renders the build timestamp.
type Options ¶
type Options struct {
Namespace string
Kit Kit
Grants *broker.Handle
Log *zap.Logger
// CacheDir persists compiled modules. Compilation is the expensive step —
// 650ms cold against 26ms cached in the spike — so it is paid at install.
CacheDir string
}
Options configure a Runner.
type Preview ¶
type Preview struct {
Manifest Manifest
Verdict *Verdict
// Grants is the plain-English list of what it will be allowed to do.
Grants []string
// Diff is set when this replaces an installed version.
Diff *CapabilityDiff
// Upgrade is the version being replaced, if any.
Upgrade string
}
Preview is what the operator is shown before anything is written.
type RegistryEntry ¶
type RegistryEntry struct {
Name string `json:"name"`
Kind Kind `json:"kind"`
Version string `json:"version"`
Description string `json:"description"`
Author string `json:"author,omitempty"`
Tags []string `json:"tags,omitempty"`
// Source is the path in the registry repo, for anyone who wants to read it
// before installing — which for an unsigned tier is the only review there is.
Source string `json:"source,omitempty"`
// Artifact is where the installable bytes are. Relative paths resolve
// against the registry base; a workflow usually points at a release.
Artifact string `json:"artifact"`
// SHA256 pins the artifact. For a workflow this is belt and braces over the
// signature; for a recipe it is the only integrity check there is, which
// makes it the more important of the two.
SHA256 string `json:"sha256,omitempty"`
// ShipWithKARMAX marks what a fresh install starts with.
ShipWithKARMAX bool `json:"ship_with_karmax,omitempty"`
// Requires names tools the entry expects to exist, so "this needs WhatsApp
// set up" is answerable before installing rather than after it fails.
Requires []string `json:"requires,omitempty"`
}
RegistryEntry is one installable thing in a registry, distinct from the lockfile Entry that records what is actually installed here.
type Runner ¶
type Runner struct {
// contains filtered or unexported fields
}
Runner executes one loop's module.
func NewRunner ¶
NewRunner compiles a verified artifact.
It takes an Artifact rather than bytes, because an Artifact is what Verify produces, and there is no way to reach this with something unverified except by constructing one deliberately.
func (*Runner) InvokeTool ¶
func (r *Runner) InvokeTool(ctx context.Context, name string, input map[string]any, timeout time.Duration) (string, error)
InvokeTool runs one of the workflow's provided tools.
It takes toolMu, NOT the mutex Run holds for the whole of a run. A workflow whose `ask` triggers its own provided tool would otherwise deadlock against itself on the very first call — the run holding the lock, waiting on an agent turn, waiting on this.
func (*Runner) ProvidedTools ¶
ProvidedTools are the tools this workflow implements, from its signed manifest. Empty for a workflow that provides none, which is most of them.
type ShortMemory ¶
ShortMemory is one short-term working note.
type Store ¶
Store is what install needs to record grants. An interface so the steps can be tested without a database.
type Tier ¶
type Tier string
Tier is how much an artifact was vouched for.
const ( // TierRegistry is countersigned by a registry key the operator trusts, // which means it went through review. TierRegistry Tier = "registry" // TierCommunity carries only a publisher signature. It runs, loudly, with // reduced defaults. TierCommunity Tier = "community" // TierUntrusted carries no valid signature at all. Nothing is known about // where it came from — only that the bytes match the manifest describing // them. It exists so a developer can run what they just built without // generating a key first, and it is never reached by accident. TierUntrusted Tier = "untrusted" )
type ToolSpec ¶
type ToolSpec struct {
Name string `json:"name" yaml:"name"`
Description string `json:"description" yaml:"description"`
Parameters json.RawMessage `json:"parameters,omitempty" yaml:"-"`
}
ToolSpec is a tool a module implements, in the shape the agent needs to call it. Inside the signature, so a module cannot widen its own description after the operator approved it.
func (*ToolSpec) UnmarshalYAML ¶
UnmarshalYAML lets a manifest write the JSON Schema as ordinary YAML. The canonical form stays JSON — that is what is signed and what the model is shown — so the schema is converted here rather than carried in two shapes.
type Trust ¶
type Trust struct {
// Registries are the countersigning keys this instance trusts. Empty means
// no artifact can reach registry tier.
Registries []string
// Revoked lists publisher or registry keys that must be refused, whatever
// else is true of the artifact.
Revoked []string
// AllowCommunity permits publisher-only artifacts. Off by default: running
// unreviewed code from a stranger should be a decision, not a default.
AllowCommunity bool
// AllowUntrusted accepts an artifact with no valid signature.
//
// Deliberately NOT a setting. It is set for ONE install, by an operator who
// typed the loop's name to confirm, and it is not written to trust.json —
// which is the difference between "I am running the thing I just built" and
// "this machine now accepts anything". The global equivalent would be a
// switch nobody remembers flipping.
AllowUntrusted bool
}
Trust is the operator's configuration for what to accept.