@newtype-ai/nit-sdk 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,11 +11,12 @@ npm install @newtype-ai/nit-sdk
11
11
  ## Usage
12
12
 
13
13
  ```typescript
14
- import { verifyAgent } from '@newtype-ai/nit-sdk';
14
+ import { verifyAgent, NitSdkError } from '@newtype-ai/nit-sdk';
15
15
 
16
16
  // The agent sends you a login payload (generated by `nit sign --login your-app.com`)
17
17
  const result = await verifyAgent(payload, {
18
- policy: { max_identities_per_machine: 10, min_age_seconds: 3600 }
18
+ policy: { max_identities_per_machine: 10, min_age_seconds: 3600 },
19
+ timeoutMs: 5_000, // optional, default 10s
19
20
  });
20
21
 
21
22
  if (result.verified && result.admitted) {
@@ -50,6 +51,7 @@ The server acts as an **identity registry** — it stores identity metadata, eva
50
51
  | `payload` | `LoginPayload` | `{ agent_id, domain, timestamp, signature }` from the agent |
51
52
  | `options.apiUrl` | `string` | Override API URL (default: `https://api.newtype-ai.org`) |
52
53
  | `options.policy` | `VerifyPolicy` | Trust rules the server evaluates (all optional) |
54
+ | `options.timeoutMs` | `number` | Fetch timeout in milliseconds (default: `10000`) |
53
55
 
54
56
  **Policy fields:**
55
57
 
@@ -74,6 +76,42 @@ The server acts as an **identity registry** — it stores identity metadata, eva
74
76
  | `identity` | `IdentityMetadata` | Registration time, machine/IP counts, login history |
75
77
  | `attestation` | `ServerAttestation` | Server's Ed25519 signature over the result |
76
78
 
79
+ ### `NitSdkError`
80
+
81
+ Typed error thrown by `fetchAgentCard()` on non-404 HTTP failures and malformed responses.
82
+
83
+ | Property | Type | Description |
84
+ |----------|------|-------------|
85
+ | `message` | `string` | Human-readable error description |
86
+ | `status` | `number` | HTTP status code (0 for shape validation failures) |
87
+
88
+ ```typescript
89
+ import { fetchAgentCard, NitSdkError } from '@newtype-ai/nit-sdk';
90
+
91
+ try {
92
+ const card = await fetchAgentCard(agentId, 'your-app.com', readToken);
93
+ } catch (err) {
94
+ if (err instanceof NitSdkError) {
95
+ console.error(`SDK error (HTTP ${err.status}): ${err.message}`);
96
+ }
97
+ }
98
+ ```
99
+
100
+ ### `fetchAgentCard(agentId, domain, readToken, options?)`
101
+
102
+ | Parameter | Type | Description |
103
+ |-----------|------|-------------|
104
+ | `options.baseUrl` | `string` | Override card hosting URL (default: `https://agent-{id}.newtype-ai.org`) |
105
+ | `options.timeoutMs` | `number` | Fetch timeout in milliseconds (default: `10000`) |
106
+
107
+ Returns `Promise<AgentCard | null>` — `null` for 404, throws `NitSdkError` for other HTTP errors.
108
+
109
+ ## Security
110
+
111
+ - **HTTPS enforcement:** Custom `apiUrl` and `baseUrl` must use `https://`. Localhost (`127.0.0.1`, `localhost`) is exempt for development. Non-HTTPS URLs throw `TypeError`.
112
+ - **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.
113
+ - **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: '...' }`.
114
+
77
115
  ## Full Integration Guide
78
116
 
79
117
  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.
package/dist/index.d.ts CHANGED
@@ -4,6 +4,11 @@
4
4
  * Apps receive a login payload from an agent (via nit) and call
5
5
  * verifyAgent() to confirm the agent's identity. No crypto needed.
6
6
  */
7
+ /** Typed error for HTTP and shape failures in the SDK. */
8
+ declare class NitSdkError extends Error {
9
+ readonly status: number;
10
+ constructor(message: string, status: number);
11
+ }
7
12
  /** The login payload an agent sends to your app. */
8
13
  interface LoginPayload {
9
14
  agent_id: string;
@@ -100,10 +105,14 @@ interface VerifyOptions {
100
105
  apiUrl?: string;
101
106
  /** App-defined trust policy. Server evaluates and returns admitted: true/false. */
102
107
  policy?: VerifyPolicy;
108
+ /** Fetch timeout in milliseconds. Defaults to 10 000. */
109
+ timeoutMs?: number;
103
110
  }
104
111
  interface FetchCardOptions {
105
112
  /** Override the base URL for agent card hosting. Defaults to https://agent-{agent_id}.newtype-ai.org */
106
113
  baseUrl?: string;
114
+ /** Fetch timeout in milliseconds. Defaults to 10 000. */
115
+ timeoutMs?: number;
107
116
  }
108
117
  /**
109
118
  * Verify an agent's login payload against the newtype-ai.org server.
@@ -139,4 +148,4 @@ declare function verifyAgent(payload: LoginPayload, options?: VerifyOptions): Pr
139
148
  */
140
149
  declare function fetchAgentCard(agentId: string, domain: string, readToken: string, options?: FetchCardOptions): Promise<AgentCard | null>;
141
150
 
142
- export { type AgentCard, type AgentCardSkill, type FetchCardOptions, type IdentityMetadata, type LoginPayload, type ServerAttestation, type VerifyFailure, type VerifyOptions, type VerifyPolicy, type VerifyResult, type VerifySuccess, fetchAgentCard, verifyAgent };
151
+ export { type AgentCard, type AgentCardSkill, type FetchCardOptions, type IdentityMetadata, type LoginPayload, NitSdkError, type ServerAttestation, type VerifyFailure, type VerifyOptions, type VerifyPolicy, type VerifyResult, type VerifySuccess, fetchAgentCard, verifyAgent };
package/dist/index.js CHANGED
@@ -1,30 +1,107 @@
1
1
  // src/index.ts
2
+ var NitSdkError = class extends Error {
3
+ constructor(message, status) {
4
+ super(message);
5
+ this.status = status;
6
+ this.name = "NitSdkError";
7
+ }
8
+ };
9
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10
+ var DEFAULT_TIMEOUT_MS = 1e4;
11
+ function assertHttps(url, label) {
12
+ try {
13
+ const parsed = new URL(url);
14
+ if (parsed.protocol === "https:") return;
15
+ if (parsed.protocol === "http:" && (parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1"))
16
+ return;
17
+ throw new TypeError(
18
+ `${label} must use HTTPS (got ${parsed.protocol}//${parsed.hostname})`
19
+ );
20
+ } catch (e) {
21
+ if (e instanceof TypeError) throw e;
22
+ throw new TypeError(`${label} is not a valid URL: ${url}`);
23
+ }
24
+ }
25
+ function validatePayload(payload) {
26
+ if (typeof payload.agent_id !== "string" || !UUID_RE.test(payload.agent_id)) {
27
+ throw new TypeError(
28
+ "payload.agent_id must be a UUID (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)"
29
+ );
30
+ }
31
+ if (typeof payload.domain !== "string" || payload.domain.length === 0 || payload.domain.length > 253) {
32
+ throw new TypeError(
33
+ "payload.domain must be a non-empty string (max 253 chars)"
34
+ );
35
+ }
36
+ if (typeof payload.timestamp !== "number" || !Number.isFinite(payload.timestamp) || payload.timestamp <= 0) {
37
+ throw new TypeError("payload.timestamp must be a finite positive number");
38
+ }
39
+ if (typeof payload.signature !== "string" || payload.signature.length === 0) {
40
+ throw new TypeError("payload.signature must be a non-empty string");
41
+ }
42
+ }
43
+ function fetchWithTimeout(url, init, timeoutMs) {
44
+ const controller = new AbortController();
45
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
46
+ return fetch(url, { ...init, signal: controller.signal }).finally(
47
+ () => clearTimeout(timer)
48
+ );
49
+ }
2
50
  var DEFAULT_API_URL = "https://api.newtype-ai.org";
3
51
  async function verifyAgent(payload, options) {
52
+ validatePayload(payload);
4
53
  const apiUrl = options?.apiUrl ?? DEFAULT_API_URL;
5
- const res = await fetch(`${apiUrl}/agent-card/verify`, {
6
- method: "POST",
7
- headers: { "Content-Type": "application/json" },
8
- body: JSON.stringify({
9
- agent_id: payload.agent_id,
10
- domain: payload.domain,
11
- timestamp: payload.timestamp,
12
- signature: payload.signature,
13
- ...options?.policy ? { policy: options.policy } : {}
14
- })
15
- });
16
- return res.json();
54
+ if (options?.apiUrl) assertHttps(apiUrl, "options.apiUrl");
55
+ const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
56
+ const res = await fetchWithTimeout(
57
+ `${apiUrl}/agent-card/verify`,
58
+ {
59
+ method: "POST",
60
+ headers: { "Content-Type": "application/json" },
61
+ body: JSON.stringify({
62
+ agent_id: payload.agent_id,
63
+ domain: payload.domain,
64
+ timestamp: payload.timestamp,
65
+ signature: payload.signature,
66
+ ...options?.policy ? { policy: options.policy } : {}
67
+ })
68
+ },
69
+ timeoutMs
70
+ );
71
+ if (!res.ok) {
72
+ return { verified: false, error: `Server error (HTTP ${res.status})` };
73
+ }
74
+ const data = await res.json();
75
+ if (typeof data !== "object" || data === null || typeof data.verified !== "boolean") {
76
+ return { verified: false, error: "Malformed server response (missing verified field)" };
77
+ }
78
+ return data;
17
79
  }
18
80
  async function fetchAgentCard(agentId, domain, readToken, options) {
19
81
  const baseUrl = options?.baseUrl ?? `https://agent-${agentId}.newtype-ai.org`;
82
+ if (options?.baseUrl) assertHttps(baseUrl, "options.baseUrl");
83
+ const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
20
84
  const url = `${baseUrl}/.well-known/agent-card.json?branch=${encodeURIComponent(domain)}`;
21
- const res = await fetch(url, {
22
- headers: { Authorization: `Bearer ${readToken}` }
23
- });
24
- if (!res.ok) return null;
25
- return res.json();
85
+ const res = await fetchWithTimeout(
86
+ url,
87
+ { headers: { Authorization: `Bearer ${readToken}` } },
88
+ timeoutMs
89
+ );
90
+ if (res.status === 404) return null;
91
+ if (!res.ok) {
92
+ throw new NitSdkError(
93
+ `Failed to fetch agent card (HTTP ${res.status})`,
94
+ res.status
95
+ );
96
+ }
97
+ const data = await res.json();
98
+ if (typeof data !== "object" || data === null || typeof data.name !== "string") {
99
+ throw new NitSdkError("Malformed agent card response (missing name field)", 0);
100
+ }
101
+ return data;
26
102
  }
27
103
  export {
104
+ NitSdkError,
28
105
  fetchAgentCard,
29
106
  verifyAgent
30
107
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newtype-ai/nit-sdk",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "Verify agent identity with one function call",
5
5
  "type": "module",
6
6
  "license": "MIT",