@treeship/sdk
TypeScript SDK for Treeship. Wraps the CLI binary for signing, verification, and hub connections.
Install
npm install @treeship/sdkThe SDK shells out to the treeship CLI binary. The CLI must be installed and initialized (treeship init) before using the SDK. WASM-based signing is planned for a future version.
Quick start
import { ship } from '@treeship/sdk';
const s = ship();
// Attest an action
const { artifactId } = await s.attest.action({
actor: 'agent://my-agent',
action: 'tool.call',
});
// Verify it
const result = await s.verify.verify(artifactId);
console.log(result.outcome); // "pass"
// Push to Hub
const { hubUrl } = await s.hub.push(artifactId);
console.log(hubUrl); // https://treeship.dev/verify/art_xxxShip class
Ship.checkCli()
Static method that checks whether the treeship CLI binary is installed and available on PATH. Returns the version string (e.g. "0.4.1"). Throws a descriptive error if the binary is not found.
import { Ship } from '@treeship/sdk';
const version = await Ship.checkCli();
console.log(`Treeship CLI version: ${version}`);Call this early in your startup sequence to fail fast if the CLI is missing, rather than getting a confusing error on the first attestation call.
Instance properties
| Property | Type | Description |
|---|---|---|
attest | AttestModule | Attestation methods (action, decision, approval, handoff) |
verify | VerifyModule | Verification methods |
hub | HubModule | Hub push/pull and connection status |
Attest module
All attestation methods live under s.attest.
s.attest.action(params)
Record that an actor performed an action.
const { artifactId } = await s.attest.action({
actor: 'agent://my-agent',
action: 'tool.call',
meta: { model: 'gpt-4o', tokens: 1247 },
});Params: ActionParams
| Field | Type | Required | Description |
|---|---|---|---|
actor | string | Yes | Actor URI, e.g. agent://my-agent |
action | string | Yes | Label for the action performed |
parentId | string | No | Parent artifact ID for chain linking |
approvalNonce | string | No | Nonce from an existing approval |
meta | Record<string, unknown> | No | Arbitrary metadata attached to the artifact |
Returns: ActionResult -- { artifactId: string }
s.attest.decision(params)
Record an LLM inference decision with token usage and confidence scoring.
await s.attest.decision({
actor: 'agent://analyst',
model: 'claude-opus-4',
tokensIn: 8432,
tokensOut: 1247,
summary: 'Contract looks standard.',
confidence: 0.91,
});Params: DecisionParams
| Field | Type | Required | Description |
|---|---|---|---|
actor | string | Yes | Actor URI |
model | string | No | Model identifier |
tokensIn | number | No | Input token count |
tokensOut | number | No | Output token count |
promptDigest | string | No | SHA-256 digest of the prompt |
summary | string | No | Plain-text summary of the decision |
confidence | number | No | Confidence score between 0 and 1 |
parentId | string | No | Parent artifact ID for chain linking |
Note: The modelVersion field exists in the TypeScript type but is not currently forwarded to the CLI. Use the model field to identify the model and version together (e.g., "claude-opus-4-20260301").
Returns: ActionResult -- { artifactId: string }
s.attest.approval(params)
Record that an approver authorized an intent. Returns a nonce that can be echoed back when attesting the approved action.
const { artifactId, nonce } = await s.attest.approval({
approver: 'human://alice',
description: 'approve payment max $500',
});Params: ApprovalParams
| Field | Type | Required | Description |
|---|---|---|---|
approver | string | Yes | Approver identity URI |
description | string | Yes | Plain-text description of what is authorized |
expires | string | No | ISO-8601 timestamp when the approval expires, e.g. "2026-12-31T00:00:00Z" |
subject | string | No | Artifact ID being approved -- maps to the CLI --subject flag |
Returns: ApprovalResult -- { artifactId: string, nonce: string }
s.attest.handoff(params)
Record a transfer of work between actors.
await s.attest.handoff({
from: 'agent://researcher',
to: 'agent://executor',
artifacts: ['art_abc123'],
});Params: HandoffParams
| Field | Type | Required | Description |
|---|---|---|---|
from | string | Yes | Source actor URI |
to | string | Yes | Destination actor URI |
artifacts | string[] | Yes | Artifact IDs being transferred |
approvals | string[] | No | Approval IDs the receiver inherits |
obligations | string[] | No | Obligations the receiver must satisfy |
Returns: ActionResult -- { artifactId: string }
Verify module
s.verify.verify(artifactId)
Verify an artifact and walk its chain.
const result = await s.verify.verify('art_abc123');
// { outcome: 'pass', chain: 3, target: 'art_abc123' }Returns: VerifyResult
| Field | Type | Description |
|---|---|---|
outcome | 'pass' | 'fail' | 'error' | Verification result |
chain | number | Number of artifacts in the chain |
target | string | The artifact ID that was verified |
Hub module
s.hub.push(artifactId)
Push an artifact to the Treeship Hub.
const { hubUrl } = await s.hub.push('art_abc123');
// https://treeship.dev/verify/art_abc123Returns: PushResult
| Field | Type | Description |
|---|---|---|
hubUrl | string | Public URL for the artifact on the Hub |
rekorIndex | number | undefined | Rekor transparency log index, if available |
s.hub.pull(id)
Pull an artifact from the Treeship Hub to the local store.
await s.hub.pull('art_abc123');Returns void. Throws if the artifact is not found or the network request fails.
s.hub.status()
Check the current hub connection status.
const status = await s.hub.status();
if (status.attached) {
console.log(`Connected to ${status.endpoint}`);
}Returns:
| Field | Type | Description |
|---|---|---|
attached | boolean | Whether the hub connection is currently active |
endpoint | string | undefined | Hub endpoint URL, if connected |
hubId | string | undefined | Hub connection identifier, if connected |
If the status check fails for any reason (CLI not installed, no config), returns { attached: false } instead of throwing.
Exported types
All types are re-exported from the top-level @treeship/sdk import.
ActionParams
interface ActionParams {
actor: string;
action: string;
parentId?: string;
approvalNonce?: string;
meta?: Record<string, unknown>;
}ApprovalParams
interface ApprovalParams {
approver: string;
description: string;
expires?: string; // ISO-8601 timestamp
subject?: string; // artifact ID being approved
}HandoffParams
interface HandoffParams {
from: string;
to: string;
artifacts: string[];
approvals?: string[];
obligations?: string[];
}DecisionParams
interface DecisionParams {
actor: string;
model?: string;
modelVersion?: string;
tokensIn?: number;
tokensOut?: number;
promptDigest?: string;
summary?: string;
confidence?: number;
parentId?: string;
}ActionResult
interface ActionResult {
artifactId: string;
}ApprovalResult
interface ApprovalResult {
artifactId: string;
nonce: string;
}VerifyResult
interface VerifyResult {
outcome: "pass" | "fail" | "error";
chain: number;
target: string;
}PushResult
interface PushResult {
hubUrl: string;
rekorIndex?: number;
}Error handling
The SDK throws TreeshipError when the CLI exits with a non-zero code.
TreeshipError
class TreeshipError extends Error {
readonly args: string[]; // the CLI arguments that were passed
readonly name: "TreeshipError";
}TreeshipError extends the standard Error class. The args property contains the exact CLI arguments that were passed, which is useful for debugging. The message property contains the stderr output from the CLI.
import { TreeshipError } from '@treeship/sdk';
try {
await s.attest.action({ actor: 'agent://x', action: 'test' });
} catch (e) {
if (e instanceof TreeshipError) {
console.error('CLI failed:', e.message);
console.error('Args:', e.args);
}
}Design rules
CLI-backed
Shells out to the treeship binary. No native crypto in the SDK yet.
Never stores sensitive content
Hash payloads, not content.
Explicit errors
CLI failures throw TreeshipError. Verify returns a typed VerifyResult with an outcome field instead of throwing.