npm package: @newtype-ai/nit-sdk
Description: Verify agent identity with one function call
Latest version: 0.5.0

# @newtype-ai/nit-sdk

Verify AI agent identity with one function call. No crypto, no OAuth, no secrets needed.

## Install

```bash
npm install @newtype-ai/nit-sdk
```

## Usage

```typescript
import { verifyAgent, NitSdkError } from '@newtype-ai/nit-sdk';

// The agent sends you a login payload (generated by `nit sign --login your-app.com`)
const result = await verifyAgent(payload, {
  policy: { max_identities_per_machine: 10, min_age_seconds: 3600 },
  timeoutMs: 5_000, // optional, default 10s
});

if (result.verified && result.admitted) {
  // result.agent_id — the agent's permanent UUID
  // result.card — the agent's card for your domain (skills, description, etc.)
  // result.wallet — { solana, evm } chain addresses
  // result.identity — registration time, machine/IP identity counts, login history
  // result.readToken — for fetching updated cards later
  console.log(`Welcome, ${result.card?.name}`);
} else if (result.verified && !result.admitted) {
  console.log('Identity verified but does not meet trust policy');
} else {
  console.log(`Verification failed: ${result.error}`);
}
```

## How It Works

1. The agent runs `nit sign --login your-app.com` to generate a signed login payload
2. The agent sends the payload to your app
3. Your app calls `verifyAgent(payload, { policy })` — this hits `api.newtype-ai.org/agent-card/verify`
4. You get back `{ verified, admitted, agent_id, card, identity, attestation, ... }` or `{ verified: false, error }`

The server acts as an **identity registry** — it stores identity metadata, evaluates your trust policy, and returns a decision alongside raw signals. Like Stripe Radar: evaluates rules server-side for convenience, returns metadata for transparency.

## API

### `verifyAgent(payload, options?)`

| Parameter | Type | Description |
|-----------|------|-------------|
| `payload` | `LoginPayload` | `{ agent_id, domain, timestamp, signature }` from the agent |
| `options.apiUrl` | `string` | Override API URL (default: `https://api.newtype-ai.org`) |
| `options.policy` | `VerifyPolicy` | Trust rules the server evaluates (all optional) |
| `options.timeoutMs` | `number` | Fetch timeout in milliseconds (default: `10000`) |

**Policy fields:**

| Field | Type | Description |
|-------|------|-------------|
| `max_identities_per_ip` | `number` | Reject if too many identities from same registration IP |
| `max_identities_per_machine` | `number` | Reject if too many identities from same machine |
| `min_age_seconds` | `number` | Reject identities younger than this (e.g., 5) |
| `max_login_rate_per_hour` | `number` | Reject if login rate is too high |

**Returns** `Promise<VerifyResult>`:

| Field | Type | Description |
|-------|------|-------------|
| `verified` | `boolean` | Ed25519 signature is valid |
| `admitted` | `boolean` | Identity meets your policy (`true` if no policy specified) |
| `agent_id` | `string` | Agent's permanent UUID |
| `card` | `AgentCard` | Agent's card for your domain |
| `branch` | `string` | Which branch the card came from (domain or `"main"`) |
| `wallet` | `{ solana, evm }` | Chain addresses |
| `readToken` | `string` | For fetching updated cards (30-day expiry) |
| `identity` | `IdentityMetadata` | Registration time, machine/IP counts, login history |
| `attestation` | `ServerAttestation` | Server's Ed25519 signature over the result |

### `NitSdkError`

Typed error thrown by `fetchAgentCard()` on non-404 HTTP failures and malformed responses.

| Property | Type | Description |
|----------|------|-------------|
| `message` | `string` | Human-readable error description |
| `status` | `number` | HTTP status code (0 for shape validation failures) |

```typescript
import { fetchAgentCard, NitSdkError } from '@newtype-ai/nit-sdk';

try {
  const card = await fetchAgentCard(agentId, 'your-app.com', readToken);
} catch (err) {
  if (err instanceof NitSdkError) {
    console.error(`SDK error (HTTP ${err.status}): ${err.message}`);
  }
}
```

### `fetchAgentCard(agentId, domain, readToken, options?)`

| Parameter | Type | Description |
|-----------|------|-------------|
| `options.baseUrl` | `string` | Override card hosting URL (default: `https://agent-{id}.newtype-ai.org`) |
| `options.timeoutMs` | `number` | Fetch timeout in milliseconds (default: `10000`) |

Returns `Promise<AgentCard | null>` — `null` for 404, throws `NitSdkError` for other HTTP errors.

### Runtime (LLM provider identity)

The `identity` object includes self-declared runtime fields — which LLM provider, model, and harness powers the agent. These are **untrusted** (the agent declares them), but useful for display and consistency checks:

```typescript
if (result.verified && result.identity) {
  // Display which LLM powers this agent (self-declared)
  console.log(`Agent powered by ${result.identity.runtime_provider} / ${result.identity.runtime_model}`);

  // Consistency check — flag if the agent has changed providers over time
  if (result.identity.distinct_runtime_providers > 1) {
    console.warn('Agent has switched LLM providers over time');
  }
}
```

Phase 2 will add cryptographic attestation for some providers (Hugging Face, ChatGPT OAuth).

## Security

- **HTTPS enforcement:** Custom `apiUrl` and `baseUrl` must use `https://`. Localhost (`127.0.0.1`, `localhost`) is exempt for development. Non-HTTPS URLs throw `TypeError`.
- **Input validation:** `verifyAgent` validates the payload before sending: `agent_id` must be a valid UUID, `domain` must be non-empty (max 253 chars), `timestamp` must be a finite positive number, `signature` must be non-empty.
- **Response shape checks:** After parsing JSON responses, the SDK verifies the expected shape (`verified` must be boolean, card must have `name` string) before returning. Malformed responses are returned as `{ verified: false, error: '...' }`.

## Full Integration Guide

See [docs/app-integration.md](docs/app-integration.md) for the complete flow, endpoint spec, code examples in multiple languages, fetching updated cards, and security notes.

## License

MIT