OrvynID
Verify an agent
Check that a caller really is a given Orvyn agent, with one API call or just a NEAR RPC.
An agent proves who it is by signing a short message as its named account (atlas.id.orvyn.near), for your audience (your domain), with an expiry. Orvyn agents send one on every call to a Bliss plugin, in the X-OrvynID-Proof header, and creators can generate one for any app.
The proof
The header is base64url of this JSON:
{
"v": 1,
"id": "atlas.id.orvyn.near",
"message": "{\"aud\":\"api.example.com\",\"exp\":1790000600,\"iat\":1790000000,\"id\":\"atlas.id.orvyn.near\",\"nonce\":\"5f2c…\",\"type\":\"orvyn-id-proof\",\"v\":1}",
"recipient": "api.example.com",
"nonce": "base64 of sha256(claim nonce)",
"publicKey": "ed25519:…",
"signature": "base64 ed25519 signature",
"evm": { "address": "0x…", "signature": "0x…" }
}messageis the signed claim: canonical JSON with sorted keys.- The signature is a NEP-413 signature of
message, withrecipient= the audience and a 32-byte nonce = sha256 of the claim's nonce. evmis an optional EIP-191 co-signature of the samemessageby the agent's EVM wallet.
Option 1: the verify endpoint
curl -s https://api.orvyn.cash/id/verify \
-H 'content-type: application/json' \
-d '{"audience":"api.example.com","proof":"<X-OrvynID-Proof value>"}'{
"valid": true,
"id": "atlas.id.orvyn.near",
"handle": "atlas",
"agentId": "…",
"owner": "alice.near",
"wallets": { "near": "…", "evm": "0x…", "solana": "…" },
"token": "atlas.nearlytrade.near",
"erc8004": { "chain": "eip155:8453", "registry": "0x8004…a432", "agent_id": "…" },
"evmVerified": true,
"claim": { "aud": "api.example.com", "iat": 1790000000, "exp": 1790000600, "nonce": "5f2c…" }
}A rejected proof returns { "valid": false, "error": "…" }, for example proof is for other.com, not api.example.com or proof has expired.
Option 2: verify it yourself
You don't have to trust Orvyn's endpoint. A proof is valid when all of these hold:
- The claim's
typeisorvyn-id-proof,audequals your audience, and now is betweeniatandexp(allow a minute of clock skew). idends in.id.orvyn.near.publicKeyis a full-access key ofid, per a NEAR RPCview_access_keycall.- The NEP-413 signature is valid for
message,recipientand the nonce. id.orvyn.near's viewis_active({ "account": id })returnstrue.- If
evmis present, it recovers to the EVM wallet in the agent's record.
With near-api-js:
import { createHash } from "node:crypto";
import { JsonRpcProvider } from "near-api-js";
import { verifyMessage } from "near-api-js/nep413";
const provider = new JsonRpcProvider({ url: "https://free.rpc.fastnear.com" });
export async function verifyOrvynId(header: string, audience: string) {
const proof = JSON.parse(Buffer.from(header, "base64url").toString());
const claim = JSON.parse(proof.message);
const now = Math.floor(Date.now() / 1000);
if (claim.type !== "orvyn-id-proof" || claim.aud !== audience || proof.recipient !== audience) throw new Error("wrong audience");
if (claim.exp < now - 60 || claim.iat > now + 60) throw new Error("expired");
if (!proof.id.endsWith(".id.orvyn.near") || claim.id !== proof.id) throw new Error("not an OrvynID");
const nonce = createHash("sha256").update(String(claim.nonce)).digest();
// Checks the key is a full-access key of the account, then the signature.
await verifyMessage({
signerAccountId: proof.id,
signerPublicKey: proof.publicKey,
payload: { message: proof.message, recipient: audience, nonce: new Uint8Array(nonce) },
signature: new Uint8Array(Buffer.from(proof.signature, "base64")),
provider,
});
const active = await provider.callFunction({ contractId: "id.orvyn.near", method: "is_active", args: { account: proof.id } });
if (!active) throw new Error("identity not active");
return { id: proof.id, handle: proof.id.split(".")[0], nonce: claim.nonce };
}Replay protection is yours. Keep the nonces you've accepted until their proof expires, and reject repeats. For requests from Orvyn agents to plugins, a proof is reused for up to 5 minutes (valid for 10), so treat it as a short-lived session for that agent, not a one-time token.
Linking an agent to your platform
To let a creator link their agent to an account on your platform, give them a nonce and your audience. On the agent page they open OrvynID → Create a proof for another app, enter your audience, and paste the proof back to you. Verify it, check the nonce is yours, and link.