# @treeship/sdk
Source: https://docs.treeship.dev/sdk/typescript

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

## Install

```bash
npm install @treeship/sdk
```

<Callout type="info">
  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.
</Callout>

## Quick start

```ts

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.

```ts

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.

```ts
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.

```ts
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.

```ts
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.

```ts
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.

```ts
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.

```ts
const { hubUrl } = await s.hub.push('art_abc123');
// https://treeship.dev/verify/art_abc123
```

**Returns: `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.

```ts
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.

```ts
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`

```ts
interface ActionParams {
  actor: string;
  action: string;
  parentId?: string;
  approvalNonce?: string;
  meta?: Record<string, unknown>;
}
```

### `ApprovalParams`

```ts
interface ApprovalParams {
  approver: string;
  description: string;
  expires?: string;   // ISO-8601 timestamp
  subject?: string;   // artifact ID being approved
}
```

### `HandoffParams`

```ts
interface HandoffParams {
  from: string;
  to: string;
  artifacts: string[];
  approvals?: string[];
  obligations?: string[];
}
```

### `DecisionParams`

```ts
interface DecisionParams {
  actor: string;
  model?: string;
  modelVersion?: string;
  tokensIn?: number;
  tokensOut?: number;
  promptDigest?: string;
  summary?: string;
  confidence?: number;
  parentId?: string;
}
```

### `ActionResult`

```ts
interface ActionResult {
  artifactId: string;
}
```

### `ApprovalResult`

```ts
interface ApprovalResult {
  artifactId: string;
  nonce: string;
}
```

### `VerifyResult`

```ts
interface VerifyResult {
  outcome: "pass" | "fail" | "error";
  chain: number;
  target: string;
}
```

### `PushResult`

```ts
interface PushResult {
  hubUrl: string;
  rekorIndex?: number;
}
```

## Error handling

The SDK throws `TreeshipError` when the CLI exits with a non-zero code.

### `TreeshipError`

```ts
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.

```ts

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

<Cards>
  <Card title="CLI-backed" description="Shells out to the treeship binary. No native crypto in the SDK yet." />

  <Card title="Never stores sensitive content" description="Hash payloads, not content." />

  <Card title="Explicit errors" description="CLI failures throw TreeshipError. Verify returns a typed VerifyResult with an outcome field instead of throwing." />
</Cards>