Treeship
SDK

@treeship/sdk

TypeScript SDK for Treeship. Wraps the CLI binary for signing, verification, and hub connections.

Install

npm install @treeship/sdk

The 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_xxx

Ship 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

PropertyTypeDescription
attestAttestModuleAttestation methods (action, decision, approval, handoff)
verifyVerifyModuleVerification methods
hubHubModuleHub 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

FieldTypeRequiredDescription
actorstringYesActor URI, e.g. agent://my-agent
actionstringYesLabel for the action performed
parentIdstringNoParent artifact ID for chain linking
approvalNoncestringNoNonce from an existing approval
metaRecord<string, unknown>NoArbitrary 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

FieldTypeRequiredDescription
actorstringYesActor URI
modelstringNoModel identifier
tokensInnumberNoInput token count
tokensOutnumberNoOutput token count
promptDigeststringNoSHA-256 digest of the prompt
summarystringNoPlain-text summary of the decision
confidencenumberNoConfidence score between 0 and 1
parentIdstringNoParent 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

FieldTypeRequiredDescription
approverstringYesApprover identity URI
descriptionstringYesPlain-text description of what is authorized
expiresstringNoISO-8601 timestamp when the approval expires, e.g. "2026-12-31T00:00:00Z"
subjectstringNoArtifact 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

FieldTypeRequiredDescription
fromstringYesSource actor URI
tostringYesDestination actor URI
artifactsstring[]YesArtifact IDs being transferred
approvalsstring[]NoApproval IDs the receiver inherits
obligationsstring[]NoObligations 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

FieldTypeDescription
outcome'pass' | 'fail' | 'error'Verification result
chainnumberNumber of artifacts in the chain
targetstringThe 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_abc123

Returns: PushResult

FieldTypeDescription
hubUrlstringPublic URL for the artifact on the Hub
rekorIndexnumber | undefinedRekor 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:

FieldTypeDescription
attachedbooleanWhether the hub connection is currently active
endpointstring | undefinedHub endpoint URL, if connected
hubIdstring | undefinedHub 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.