Agent Integration Guide

This guide explains how to integrate skills and agents with Clauth using the client SDK.

Installation

# From the clauth project

npm run build

Skills import from the clauth/client export:

import { ClauthClient } from "clauth/client";

Quick Start

import { ClauthClient } from "clauth/client";

// Auto-discovers from CLAUTH_DAEMON_URL, CLAUTH_SKILL_ID, CLAUTH_SKILL_TOKEN

const clauth = new ClauthClient();

// Make a brokered API call

const res = await clauth.fetch(

"github", // provider name

"github-main", // credential handle

"github:read", // required scope

"https://api.github.com/user"

);

console.log(res.status); // 200

console.log(res.body); // { login: "octocat", ... }

Configuration

Environment Variables (recommended)

export CLAUTH_DAEMON_URL=http://127.0.0.1:4317

export CLAUTH_SKILL_ID=my-agent

export CLAUTH_SKILL_TOKEN=clauth_sk_xxx

The client auto-discovers these:

const clauth = new ClauthClient();  // reads from env vars

Explicit Options

const clauth = new ClauthClient({

daemonUrl: "http://127.0.0.1:4317",

skillId: "my-agent",

skillToken: "clauth_sk_xxx"

});

API

clauth.fetch(provider, credential, scope, endpoint, options?)

Execute a brokered API request through the proxy.

Parameters:

ParamTypeDescription

providerstringProvider name (e.g., "github", "slack")
credentialstringCredential handle stored in the vault
scopestringRequired scope (e.g., "github:read")
endpointstringFull upstream URL
options.methodstringHTTP method (default: "GET")
options.headersobjectAdditional headers (auth headers stripped)
options.bodyanyRequest body (objects JSON-serialized)

Returns: { status: number, headers: object, body: any } Examples:
// GET request

const user = await clauth.fetch(

"github", "github-main", "github:read",

"https://api.github.com/user"

);

// POST request with body

const issue = await clauth.fetch(

"github", "github-main", "github:write",

"https://api.github.com/repos/owner/repo/issues",

{

method: "POST",

body: { title: "Bug report", body: "Description here" }

}

);

// Custom headers

const res = await clauth.fetch(

"custom", "custom-api", "custom:read",

"https://api.custom.example.com/data",

{

headers: { "x-custom-header": "value" }

}

);

clauth.health()

Check if the daemon is reachable.

const ok = await clauth.health();

console.log(ok); // true

clauth.status()

Get daemon status information.

const info = await clauth.status();

console.log(info.vaultUnlocked); // true

console.log(info.activeGrants); // 5

console.log(info.auditIntegrity); // { valid: true, entries: 42 }

clauth.createIdentityChallenge(provider, accountId)

Create an identity verification challenge.

const challenge = await clauth.createIdentityChallenge("github", "octocat");

console.log(challenge.challengeId); // UUID

console.log(challenge.challenge); // base64url token

console.log(challenge.expiresAt); // ISO timestamp

clauth.verifyIdentity(challengeId, proof)

Submit proof for a challenge.

// For signed-challenge: proof is the credential handle

const result = await clauth.verifyIdentity(challenge.challengeId, "github-main");

console.log(result.status); // "verified"

console.log(result.verifiedAt); // ISO timestamp

// For email: proof is the challenge code

const result = await clauth.verifyIdentity(challenge.challengeId, challenge.challenge);

clauth.getIdentityStatus(challengeId)

Poll challenge status.

const status = await clauth.getIdentityStatus(challenge.challengeId);

console.log(status.status); // "pending" | "verified" | "expired" | "failed"

clauth.listIdentityProofs()

List verified identity proofs for this skill.

const proofs = await clauth.listIdentityProofs();

for (const proof of proofs) {

console.log(${proof.provider}/${proof.accountId}: ${proof.method});

}

Error Handling

All client methods throw ClauthError on failure:

import { ClauthClient, ClauthError } from "clauth/client";

try {

await clauth.fetch("github", "handle", "github:write", "https://api.github.com/user");

} catch (error) {

if (error instanceof ClauthError) {

console.error(error.code); // "ACCESS_DENIED"

console.error(error.message); // "No active grant..."

console.error(error.statusCode); // 403

}

}

Common error codes:

CodeMeaning

ACCESS_DENIEDScope not granted or firewall blocked
UNAUTHORIZEDMissing or invalid skill token
NOT_FOUNDCredential handle or challenge not found
VALIDATION_ERRORInvalid request format
UPSTREAM_ERRORProvider API call failed

Integration Patterns

Basic Agent Pattern

import { ClauthClient, ClauthError } from "clauth/client";

const clauth = new ClauthClient();

async function fetchGitHubRepos(): Promise<any[]> {

try {

const res = await clauth.fetch(

"github", "github-main", "github:read",

"https://api.github.com/user/repos"

);

return res.body as any[];

} catch (error) {

if (error instanceof ClauthError && error.code === "ACCESS_DENIED") {

console.log("Insufficient permissions for this operation");

return [];

}

throw error;

}

}

Multi-Provider Agent

const clauth = new ClauthClient();

// GitHub API

const repos = await clauth.fetch(

"github", "github-main", "github:read",

"https://api.github.com/user/repos"

);

// Slack API

const channels = await clauth.fetch(

"slack", "slack-bot", "slack:channels",

"https://slack.com/api/conversations.list"

);

// Post to Slack

await clauth.fetch(

"slack", "slack-bot", "slack:messages",

"https://slack.com/api/chat.postMessage",

{

method: "POST",

body: { channel: "C123", text: Found ${repos.body.length} repos }

}

);

Identity-Aware Agent

const clauth = new ClauthClient();

// Verify GitHub identity before performing sensitive operations

async function ensureVerified(): Promise<boolean> {

const proofs = await clauth.listIdentityProofs();

if (proofs.some(p => p.provider === "github")) {

return true;

}

const challenge = await clauth.createIdentityChallenge("github", "octocat");

const result = await clauth.verifyIdentity(challenge.challengeId, "github-main");

return result.status === "verified";

}

Polling Pattern for Identity

async function waitForVerification(challengeId: string, timeoutMs = 300000): Promise<boolean> {

const start = Date.now();

while (Date.now() - start < timeoutMs) {

const status = await clauth.getIdentityStatus(challengeId);

if (status.status === "verified") return true;

if (status.status === "failed" || status.status === "expired") return false;

await new Promise(r => setTimeout(r, 2000));

}

return false;

}

Setup Checklist

For a new skill/agent to work with Clauth:

  1. Admin issues a skill token:

   npm run cli -- skill-token issue --skill my-agent

  1. Admin stores required credentials:

   npm run cli -- store --handle github-main --provider github --secret ghp_xxx

  1. Admin grants scopes:

   npm run cli -- grant --skill my-agent --provider github --scope github:read --rpm 60

npm run cli -- grant --skill my-agent --provider github --scope github:write --rpm 30

  1. Skill configures environment:

   export CLAUTH_SKILL_ID=my-agent

export CLAUTH_SKILL_TOKEN=<issued-token>

export CLAUTH_DAEMON_URL=http://127.0.0.1:4317

  1. Skill uses the client SDK to make brokered requests.

What Skills Cannot Do