@keyward/sdk
One client, one method. The SDK never holds an upstream secret — it asks the broker for a scoped, short-lived credential and caches it client-side until it nears expiry, so a tight tool-call loop hits the control plane once, not every call.
createClient(config)
client.get(provider, scope, ttl?, opts?)
Returns a Credential: the signed token itself plus token_type, provider, scope, expires_at, and the delegation pair (user + agent). TTL defaults to 5 minutes; the broker enforces its own ceiling. Denials throw KeywardError with the broker's status and reason.
const cred = await kw.get("stripe", "charges:read", "5m");
await fetch("https://api.stripe.com/v1/charges", {
headers: { authorization: `${cred.token_type} ${cred.credential}` },
});Delegation chains
A sub-agent acting under an orchestrator passes the orchestrator's credential instead of a user token. The broker verifies it, keeps the original human sub, and extends the RFC 8693 actor chain — nested, splice-resistant, depth-capped at 5.
// orchestrator got `cred` the normal way; hands it to the sub-agent
const sub = createClient({ controlPlaneUrl, token: "", agent: "sub-agent" });
const delegated = await sub.get("stripe", "charges:read", "5m", {
delegation: cred.credential,
});
// delegated.credential's chain: user → orchestrator → sub-agentAttested agents (secret zero)
If an agent is registered with an attestation_issuer, the broker fails closed unless the request carries a verifiable workload-identity token (GitHub Actions OIDC, Kubernetes service account, cloud instance identity). Pass a function to mint a fresh one per request:
const kw = createClient({
controlPlaneUrl, token, agent: "ci-bot",
agentAssertion: () => getGithubOidcToken(), // fresh per request
});Verifying issued credentials
Credentials are RS256 JWTs with a kid header. Resource servers verify them against the broker's public JWKS at GET /v1/jwks — no shared secret needed.