Quickstart

Quickstart

Create a service, a worker profile and a client, start a Python worker, and call it from the CLI and from Node.js.

Distro Current 4 min read Updated

Prerequisites

  • A running Kiwi Distro middleware with its HTTPS URL, and its CA endpoint if it serves one
  • The kiwi command-line shell with the Kiwi Distro CLI registered
  • Python 3.11 or newer, and the Python worker SDK

Note

Kiwi Distro is currently in private preview. Interfaces and behaviour described here can change before general availability. Contact Kiwify Labs for access.

You will create the three records every deployment needs, run a worker, and call it.

1. Sign in as an administrator#

bash
kiwi distro signin --credentials-file admin-creds.json

admin-creds.json holds client_id and client_secret. The token is cached for later commands. (You can also pass --client-id and --client-secret.)

2. Create a service and a worker profile#

bash
kiwi distro create-service --name hello-svc --desc "Quickstart service" --output json
kiwi distro create-worker-profile --profile-name hello-worker --service-id <service-id> --output json

The worker profile output includes an auth_secret, shown once. Save it.

3. Register a client for callers#

bash
kiwi distro register-client --name quickstart-client --roles <roles> --output json

The output includes the client's client_secret, also shown once. --roles takes a comma-separated list of role names, as set up for your middleware.

4. Start a worker#

Choose your language. The choice applies to every sample on the site and is remembered in your browser. Each worker authenticates, registers its methods for the service named in its credentials, then waits for work.

.NET
using Kiwify.Kiwi.Distro.Worker.Extensions;
using Kiwify.Kiwi.Distro.Worker.MethodHandler;
using Kiwify.Kiwi.Distro.Worker.MethodHandler.Attributes;
using Microsoft.Extensions.Hosting;

var builder = Host.CreateApplicationBuilder(args);
builder.AddKiwiWorker(context => context.AddMethodHandlers(), args);
builder.Build().RunKiwiWorker();

[MethodName("hello")]
public class HelloMethod : MethodHandlerBase
{
    public string Name { get; set; } = "world";

    protected override void Execute() => SetResult(new { message = $"Hello {Name}!" });
}
Java
import java.util.List;
import java.util.Map;
import kiwify.kiwi.distro.worker.*;

KiwiWorker worker = KiwiWorker.builder()
    .set("middleware_url", "https://localhost:8443")
    .set("middleware_ca_url", "http://localhost:8080/ca/cert")   // pins the middleware's CA
    .set("auth_secret", "<worker-profile-secret>")
    .set("worker_id", "hello-worker-1")
    .build();

worker.method("hello",
    List.of(ParameterSpec.of("name").type(ParameterSpec.Type.STRING).withDefault("world")),
    ctx -> Map.of("message", "Hello " + ctx.param("name") + "!"));

worker.start();
worker.awaitStop();
Node.js
import { KiwiWorker } from "@kiwify/kiwi-distro-worker";

const worker = new KiwiWorker({
  middleware_url: "https://localhost:8443",
  middleware_ca_url: "http://localhost:8080/ca/cert",   // pins the middleware's CA
  auth_secret: "<worker-profile-secret>",
  worker_id: "hello-worker-1",
});

worker.method(
  "hello",
  { parameters: [{ name: "name", type: "string", default: "world" }] },
  (ctx) => ({ message: `Hello ${ctx.param("name")}!` }),
);

await worker.start();
await worker.waitUntilStopped();
Python
import asyncio
from kiwify_kiwi_distro_worker import KiwiWorker, ParameterSpec

async def main():
    worker = KiwiWorker({
        "middleware_url": "https://localhost:8443",
        "middleware_ca_url": "http://localhost:8080/ca/cert",   # pins the middleware's CA
        "auth_secret": "<worker-profile-secret>",
        "worker_id": "hello-worker-1",
    })
    worker.method(
        "hello",
        lambda ctx: {"message": f"Hello {ctx.param('name')}!"},
        parameters=[ParameterSpec("name", type="string", default="world")],
    )
    await worker.run_forever()

asyncio.run(main())

In .NET, put the same settings (middleware_url, middleware_ca_url, auth_secret, worker_id) in appsettings.json, in KIWI_-prefixed environment variables or on the command line.

The worker authenticates, registers its methods for the service named in its credentials, then waits for work.

5. Call it#

From the CLI:

bash
kiwi distro request-method --method hello-svc/hello --target any --params "name='Ada'" --output json

From code, in the language you prefer:

.NET
using Kiwify.Kiwi.Distro.Client.Native.Core;

var factory = new MiddlewareClientFactory("https://localhost:8443", "http://localhost:8080/ca/cert");
factory.CreateAuthProviderBuilder().SetClientCredentials("<client-id>", "<client-secret>").Build();
factory.CreateHttpBuilder().Build();

var res = await factory.CreateMethodRequestHandler("hello-svc/hello")
    .AttachParameters(new Dictionary<string, object> { ["name"] = "Ada" })
    .InvokeAnyAsync();
if (res.IsSuccess) Console.WriteLine(res.Result);   // { message = Hello Ada! }
Java
import kiwify.kiwi.distro.client.*;
import java.util.Map;

KiwiClient client = KiwiClient.builder()
    .middlewareUrl("https://localhost:8443")
    .middlewareCaUrl("http://localhost:8080/ca/cert")
    .clientId("<client-id>")
    .clientSecret("<client-secret>")
    .connect();

Map<String, Object> res = client.method("hello-svc/hello")
    .params(Map.of("name", "Ada")).invokeAny();
if ("success".equals(res.get("status"))) System.out.println(res.get("result"));   // {message=Hello Ada!}
client.close();
Node.js
import { KiwiClient } from "@kiwify/kiwi-distro-client";

const client = await KiwiClient.connect({
  middlewareUrl: "https://localhost:8443",
  middlewareCaUrl: "http://localhost:8080/ca/cert",
  clientId: "<client-id>",
  clientSecret: "<client-secret>",
});
const res = await client.method("hello-svc/hello").params({ name: "Ada" }).invokeAny();
if (res.status === "success") console.log(res.result);   // { message: "Hello Ada!" }
client.close();
Python
from kiwify_kiwi_distro_client import KiwiClient

client = KiwiClient.connect(
    middleware_url="https://localhost:8443",
    middleware_ca_url="http://localhost:8080/ca/cert",
    client_id="<client-id>",
    client_secret="<client-secret>",
)
res = client.method("hello-svc/hello").params(name="Ada").invoke_any()
if res["status"] == "success":
    print(res["result"])   # {'message': 'Hello Ada!'}
client.close()

What just happened#

The client authenticated and asked the middleware for hello-svc/hello. The middleware queued the request, signalled the worker, the worker handled it and posted the result, and the middleware returned it to the client. See Architecture.

Next#

Expected result

  • The Python worker registers and answers a hello call from the CLI and from a client SDK