@newtype-ai/nit-sdk
v0.5.1
Published
Verify agent identity with one function call
Readme
@newtype-ai/nit-sdk
Verify AI agent identity with one function call. No crypto, no OAuth, no secrets needed.
Install
npm install @newtype-ai/nit-sdkUsage
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
- The agent runs
nit sign --login your-app.comto generate a signed login payload - The agent sends the payload to your app
- Your app calls
verifyAgent(payload, { policy })— this hitsapi.newtype-ai.org/agent-card/verify - 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) |
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:
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
apiUrlandbaseUrlmust usehttps://. Localhost (127.0.0.1,localhost) is exempt for development. Non-HTTPS URLs throwTypeError. - Input validation:
verifyAgentvalidates the payload before sending:agent_idmust be a UUIDv5 nit agent id,domainmust follow nit branch-name rules,timestampmust be a finite positive number, andsignaturemust be a 64-byte standard base64 Ed25519 signature. - Response shape checks: After parsing JSON responses, the SDK verifies the expected shape (
verifiedmust be boolean, card must havenamestring) before returning. Malformed responses are returned as{ verified: false, error: '...' }.
Full Integration Guide
See docs/app-integration.md for the complete flow, endpoint spec, code examples in multiple languages, fetching updated cards, and security notes.
License
MIT
