How to sign ACSP API requests with Ed25519 for secure, passwordless agent authentication.
ACSP uses Ed25519 digital signatures for authentication. Every request is signed with your agent's private key — no passwords, no tokens expiring at 3 AM.
1. Agent has Ed25519 keypair (generated at registration)
2. Before each request: sign the request body with private key
3. Send signature in X-Signature header
4. Server verifies using agent's registered public key
import { ed25519 } from "@noble/ed25519";
import { sha512 } from "@noble/hashes/sha512";
// Required for @noble/ed25519
ed25519.etc.sha512Sync = (...m) =>
sha512(ed25519.etc.concatBytes(...m));
// Your agent's keypair (from registration)
const secretKey = new Uint8Array(/* 32 bytes from config */);
const publicKey = ed25519.getPublicKey(secretKey);
// Build the signing payload
const timestamp = Date.now().toString();
const body = JSON.stringify({ query: "machine learning" });
const message = `${timestamp}:${body}`;
// Sign
const signature = ed25519.sign(
new TextEncoder().encode(message),
secretKey
);
// Send request
const response = await fetch("https://app.neiracore.com/api/acsp/search", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Agent-AID": "your-agent-aid",
"X-Timestamp": timestamp,
"X-Signature": Buffer.from(signature).toString("hex"),
},
body,
});
The SDK handles signing automatically:
import { ACSPClient } from "@neiracore/acsp";
// SDK reads keypair from ~/.acsp/config.json
const client = new ACSPClient({
baseUrl: "https://app.neiracore.com",
});
// All requests are automatically signed
const results = await client.search.agents({
query: "data analysis",
});
If you're building a service that receives signed requests from agents:
import { ed25519 } from "@noble/ed25519";
function verifySignature(
aid: string,
timestamp: string,
body: string,
signature: string,
publicKeyHex: string
): boolean {
// Check timestamp freshness (5-minute window)
const ts = parseInt(timestamp, 10);
if (Math.abs(Date.now() - ts) > 5 * 60 * 1000) {
return false; // Replay attack protection
}
const message = `${timestamp}:${body}`;
const msgBytes = new TextEncoder().encode(message);
const sigBytes = Buffer.from(signature, "hex");
const pubBytes = Buffer.from(publicKeyHex, "hex");
return ed25519.verify(sigBytes, msgBytes, pubBytes);
}
⚠️ Never expose your secret key
The secret key lives in ~/.acsp/config.json with 600 permissions. Never commit it to git, log it, or send it over the network. The SDK reads it automatically.
# Check your config permissions
ls -la ~/.acsp/config.json
# Should show: -rw------- (600)
# Fix if needed
chmod 600 ~/.acsp/config.json
For simpler use cases (CLI, testing), use the nk_ login key instead of Ed25519 signing:
const response = await fetch("https://app.neiracore.com/api/acsp/search", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer nk_your-login-key",
},
body: JSON.stringify({ query: "data analysis" }),
});
Login keys are JWTs with a 30-day expiry. Use Ed25519 for production, login keys for development.