Yoke
SDKs

An SDK for your language.

Five families. Each covers every contract it offers, whole — and the conformance suite is what says so.

go
Go
Go module proxy
PluginInterfaceAdmin

All three contracts in one module: Plugins, interface clients and administrative tooling. The reference clients are written with it.

go get github.com/yoke-project/yoke-sdk-go/pluginyoke-sdk-go
rs
Rust
crates.io
Plugin

Plugins, on Tokio, in one crate over a base the libraries to come will share.

cargo add yoke-sdkyoke-sdk-rust
py
Python
PyPI
Plugin

Plugins, on asyncio, in one wheel over a base the libraries to come will share.

pip install yoke-sdkyoke-sdk-python
c
C
source, built with CMake
PluginInterface · soon

Plugins, in C11, with protobuf-c and gRPC's core C API. The interface library follows, for panels drawn with LVGL.

gh release download -R yoke-project/yoke-sdk-cyoke-sdk-c
c++
C++
source, built with CMake
PluginInterface · soon

Plugins, in C++17, with protoc and gRPC's C++ plugin. The interface library follows, for panels drawn with Qt.

gh release download -R yoke-project/yoke-sdk-cppyoke-sdk-cpp

How it works

plugininterfaceadminGoRustPythonCC++offered, wholecomingnot offered
01

Pick the family for your language

Go, Rust, Python, C or C++, each its own repository. Every family offers the plugin contract; Go offers the interface and administrative ones too, and C and C++ have the interface contract coming. A contract a family offers, it covers whole.

Declaration {id: com.example…clockqueries: clock.nowcapabilities: …}start(declaration)writesyamlManifestspeaksplugin contract
02

Write against the contract

A Plugin declares in code what it is — its queries, commands, streams and the capabilities that govern them — and the same declaration writes its Manifest. Each family is shaped the way its own language is written, not as a copy of the others.

clientCoreclocklaunched · admittedrouted, never decoded
03

Run it under the Core

The Core launches it, admits it against what the operator granted, and routes its messages without reading what they carry.

A Plugin in each language

A clock that answers one question — the time now — written with each family. It declares the query and the capability that governs it, and the same declaration writes its Manifest.

main.go
package main

import (
	"context"
	"os"
	"strconv"
	"time"

	"github.com/yoke-project/yoke-sdk-go/plugin"
)

var declaration = plugin.Declaration{
	ID:      "com.example.hello.clock",
	Queries: []string{"clock.now"},
	Capabilities: []plugin.Capability{
		{Name: "query.now.answer", Governs: plugin.Object{Query: "clock.now"}},
	},
}

func main() {
	if len(os.Args) == 2 && os.Args[1] == "manifest" {
		os.Stdout.Write(declaration.Manifest())
		return
	}
	u, err := plugin.Start(context.Background(), declaration)
	if err != nil {
		panic(err)
	}
	u.Health(0, "keeping time")
	for event := range u.Events() {
		if q, ok := event.(plugin.Question); ok && q.Type == "clock.now" {
			u.Answer(q, []byte(strconv.FormatInt(time.Now().Unix(), 10)))
		}
	}
}
Build it
$ mkdir clock && cd clock  # main.go goes here
$ go mod init example.com/clock
$ go get github.com/yoke-project/yoke-sdk-go/plugin
$ go build -o clock .
$ ./clock manifest
manifest: 1
id: com.example.hello.clock
protocol: 1
queries:
    - id: clock.now
capabilities:
    - name: query.now.answer
      governs:
        query: clock.now

Run it — the same for every language

Terminal
$ mkdir hello && cd hello
$ yoke-compose init com.example.hello
$ install -D ../clock/clock components/plugins/clock
$ components/plugins/clock manifest > manifests/com.example.hello.clock.yaml
$ yoke-compose add unit clock --kind plugin --plugin com.example.hello.clock --exec components/plugins/clock
$ yoke-compose build --out ..
$ yoke app install ../com.example.hello-*.yoke
$ yoke instance create com.example.hello --name bench
$ yoke instance start bench
$ until yoke core status --instance bench > /dev/null 2>&1; do sleep 1; done  # start asks the guardian; the Core answers a moment later
$ yoke plugin grant com.example.hello.clock query.now.answer

The Plugin is admitted with nothing granted: its declaration asks, the operator grants. Once the grant reaches it at its next admission, any interface allowed to may ask it the time.