§ Agent Auth · Controlled beta

AI Agent Auth

AI Agent Auth is the direct runtime lane for agent apps, desktop harnesses, and tool providers that need scoped user-approved delegation without handing provider API keys to the agent runtime.

§ Agent Auth · Runtime

Pair with a runtime-generated QR

Direct runtimes generate a key-bound QR locally. The user scans that QR in Nuvouch mobile, receives a one-time code, and gives the code back to the runtime. The runtime claims the connection by signing the claim payload with the same private key that created the QR.

runtime-pairing.ts
@nuvouch/agent-auth
import {
  buildAgentAuthRuntimeClaimPayload,
  createAgentAuthRuntimeClient,
  createAgentAuthRuntimePairingRequest,
  signAgentAuthRuntimePayload,
} from "@nuvouch/agent-auth";

const runtime = createAgentAuthRuntimeClient();
const pairing = createAgentAuthRuntimePairingRequest({
  applicationId: "app_openclaw",
  runtimeDisplayName: "OpenClaw Desktop",
  deviceName: "Ada's MacBook",
});

// Render pairing.qrData as a QR code.
// The user scans it in Nuvouch mobile and enters the generated code.
const code = "NVP1-...";
const claimPayload = buildAgentAuthRuntimeClaimPayload({ code });

const connection = await runtime.claimConnection({
  code,
  publicKeyFingerprint: pairing.publicKeyFingerprint,
  signature: signAgentAuthRuntimePayload({
    privateKey: pairing.keyPair.privateKey,
    keyAlgorithm: pairing.keyPair.keyAlgorithm,
    payload: claimPayload,
  }),
});

console.log(connection.id, connection.status);
§ Agent Auth · Delegation

Request, wait for, and revoke delegation

request-delegation.ts
@nuvouch/agent-auth
import {
  buildAgentAuthDelegationReadPayload,
  buildAgentAuthDelegationRequestPayload,
  createAgentAuthNonce,
  signAgentAuthRuntimePayload,
} from "@nuvouch/agent-auth";

const nonce = createAgentAuthNonce("delreq");
const requestPayload = buildAgentAuthDelegationRequestPayload({
  runtimeConnectionId: connection.id,
  audienceId: "aud_test_payments",
  agentId: "agent_billing",
  purpose: "Identify the user before preparing a payment approval.",
  scopes: ["payments.identity", "payments.prepare"],
  nonce,
});

const delegation = await runtime.requestDelegation({
  runtimeConnectionId: connection.id,
  audienceId: "aud_test_payments",
  agent: {
    id: "agent_billing",
    name: "Billing Agent",
  },
  purpose: "Identify the user before preparing a payment approval.",
  scopes: ["payments.identity", "payments.prepare"],
  nonce,
  signature: signAgentAuthRuntimePayload({
    privateKey: pairing.keyPair.privateKey,
    keyAlgorithm: pairing.keyPair.keyAlgorithm,
    payload: requestPayload,
  }),
});

const approved = await runtime.waitForDelegation({
  runtimeConnectionId: connection.id,
  delegationId: delegation.id,
  sign: ({ nonce }) => {
    const payload = buildAgentAuthDelegationReadPayload({
      runtimeConnectionId: connection.id,
      delegationId: delegation.id,
      nonce,
    });
    return signAgentAuthRuntimePayload({
      privateKey: pairing.keyPair.privateKey,
      keyAlgorithm: pairing.keyPair.keyAlgorithm,
      payload,
    });
  },
});

if (!approved.token) {
  throw new Error(`Delegation was ${approved.status}`);
}

Revoke the runtime connection

revoke-runtime.ts
import {
  buildAgentAuthRuntimeConnectionRevokePayload,
  createAgentAuthNonce,
  signAgentAuthRuntimePayload,
} from "@nuvouch/agent-auth";

const revokeNonce = createAgentAuthNonce("revoke");
const revokePayload = buildAgentAuthRuntimeConnectionRevokePayload({
  runtimeConnectionId: connection.id,
  nonce: revokeNonce,
});

await runtime.revokeConnection({
  runtimeConnectionId: connection.id,
  nonce: revokeNonce,
  signature: signAgentAuthRuntimePayload({
    privateKey: pairing.keyPair.privateKey,
    keyAlgorithm: pairing.keyPair.keyAlgorithm,
    payload: revokePayload,
  }),
  reason: "User signed out of runtime",
});
§ Agent Auth · Provider

Introspect tokens before using a tool

Providers use their own Nuvouch API key to introspect delegation tokens. Accept only active tokens with the expected audience and scopes, then map the returned scoped subject to a local account.

provider-introspection.ts
@nuvouch/node
import { Nuvouch } from "@nuvouch/node";

const nuvouch = new Nuvouch({
  apiKey: process.env.NUVOUCH_PROVIDER_API_KEY,
});

const delegation = await nuvouch.agentAuth.provider.requireDelegation({
  token: req.headers.authorization?.replace(/^Bearer\s+/i, "") ?? "",
  audienceKey: "stripe:payments",
  requiredScopes: ["payments.prepare"],
});

const localAccount = await mapScopedSubjectToLocalAccount(delegation.subject);

Create final approval separately

final-approval.ts
@nuvouch/node
const approval = await nuvouch.approvals.create({
  targetSubject: { subjectId: delegation.subject },
  subject: { id: localAccount.id },
  source: { key: "stripe:payments", name: "Stripe Payments" },
  action: {
    type: "payment.prepare",
    title: "Approve payment",
    description: "Allow Stripe Payments to prepare the requested payment.",
  },
  actor: {
    type: "ai_agent",
    id: delegation.agent.id,
    name: delegation.agent.name,
  },
  decisions: [
    { label: "Approve", value: "approve" },
    { label: "Deny", value: "deny" },
  ],
  agentAuth: nuvouch.agentAuth.provider.buildAgentAuthApprovalContext(delegation),
});
§ Agent Auth · CLI

Use the Nuvouch CLI

The CLI is useful for local agent runtimes, demos, and controlled-beta provider testing. It stores runtime profiles in ~/.nuvouch/config.json.

FieldTypeDescription
nuvouch auth paircommandGenerate a runtime QR and claim the connection after mobile scan/code entry.
nuvouch auth statuscommandShow the active local runtime profile.
nuvouch auth requestcommandRequest scoped delegation for an audience and purpose.
nuvouch auth waitcommandWait for delegation approval and show redacted token state by default.
nuvouch auth tokencommandPrint an approved delegation token for provider calls.
cli.sh
@nuvouch/cli
nuvouch auth pair \
  --application-id app_openclaw \
  --runtime-name "OpenClaw Desktop"

nuvouch auth request \
  --audience aud_test_payments \
  --scope payments.identity \
  --scope payments.prepare \
  --purpose "Identify the user before preparing a payment approval"

nuvouch auth wait del_123 --json
nuvouch auth token del_123