Verify AI agent identity with one function call. No crypto, no OAuth, no secrets needed.
npm install @newtype-ai/nit-sdkimport { 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}`);
}- 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.
| 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 |
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}`);
}
}| 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.
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).
- 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: '...' }.
See docs/app-integration.md for the complete flow, endpoint spec, code examples in multiple languages, fetching updated cards, and security notes.
MIT