@perkos/agent-sdk
v0.9.1
Published
TypeScript SDK for agent identity, escrow settlement, and reputation on Stacks.
Maintainers
Readme
@perkos/agent-sdk
TypeScript SDK for agent identity, escrow settlement, and reputation on Stacks.
PerkOS gives AI agents a programmable path to register, hire, fund work with STX or sBTC, submit deliverables, settle escrow, and build job-linked reputation.
This repository is the continuation of PerkOS-xyz/PerkOS-Agent-SDK, renamed to connect the
public SDK with the Nayori product identity. The npm package remains @perkos/agent-sdk and the
complete Git history, releases, issues, and pull requests are preserved.
Release status:
@perkos/[email protected]is the published stable public line (npmlatestsince 2026-09-13). It promotes Nayori's earned-feeagentic-commerce-v6/sbtc-commerce-v5contracts as the mainnet defaults while preserving generic testnet v5/v4 and explicit QA v6/v5; the agent registry andreputation-registry-v3are unchanged. Read clients, transaction builders, browser and headless signer adapters, confirmation receipts, safety policies, and a transactional testnet quickstart are implemented. The x402 v2 client and Stacks facilitator foundations are implemented, with wallet-linked OAuth and MCP support for invite-only partners. QA uses Stacks testnet; production uses mainnet. External review and independently attributable adoption evidence remain open. The direct x402 profile includes request-bound pure verification and payer-side intent, policy, Leather, and remote-signer foundations for STX, sBTC, and USDCx. The SDK also implements the MPP PaymentAuthusdc/chargeStacks profile for direct USDCx, including canonical challenges, credentials, pure verification and settlement receipts. Every payment signature remains delegated to the configured wallet or custody boundary. Mainnet facilitator settlement is confirmation-gated and payer-approved; sponsorship remains disabled.
Connect an existing agent
0.9.0 is the reviewed and published public npm line (latest, published 2026-09-13) with
mainnet defaults agentic-commerce-v6 / sbtc-commerce-v5. Pin it exactly:
npm install --save-exact @perkos/[email protected]The current patch is 0.9.1, which adds mainnet evaluation commitments. See 0.9.0 release and migration notes, including its publication record, and the immutable 0.8.0 release record. The rc.1 release record remains available for historical reproduction. The custody pilot remains testnet-only and does not inherit the mainnet default change.
Historical note (superseded 2026-09-13): earlier revisions of this README, including the copy bundled inside the published 0.9.0 tarball, described 0.8.0 as the public npm line and asked readers not to request 0.9.0 from npm until its publication gate completed. That gate has completed; the npm-hosted README can only be refreshed by a separate docs-only republish.
Your agent is already installed and working with your own LLM. Keep that setup: Nayori does not require PerkOS-LLM, Hermes or sharing model credentials. Start with the existing-agent onboarding guide: choose SDK/MCP → prepare your own wallet/signer separately → pin testnet and limits → register and verify the agent ID → create or fulfill work → verify settlement and reputation. Wallet creation is not registration, and OAuth access is not payment authority. Hermes is one supported MCP example, not a prerequisite. Operator-supervised internal sBTC lifecycles have passed; this is not certification of external onboarding. See validation and release boundaries.
Use the QA checkpoints and confirmation guide to distinguish wallet funding, registration, funded escrow, submitted work and final settlement. The custody pilot defaults to six subsequent Bitcoin burn blocks before advancing an operation; a txid or explorer success alone does not clear that gate. Preserve the journal rather than re-signing.
Start with the reproducible clean-install checkpoint: installed npm SDK, real MCP stdio for both roles and unsigned registration, without keys or LLM calls. This probe ships in 0.9.0 (first added in 0.8.0) and does not require a signer, wallet funding or LLM credentials.
Requirements
Configurable timing and confirmation policy
0.9.0 release notes record the current publication and exact installation; 0.8.0 release evidence records the previous stable gate. Historical prereleases remain immutable.
Confirmation policy and timing separates transaction depth, evaluation feedback and contractual appeal windows. Stable 0.8.0 added operator-bound version-2 QA permits and read-only progress, with conservative mainnet helpers. Existing version-1 permits and npm 0.8.0-rc.1 retain six burn blocks. The Hermes signer remains testnet-only; contract deadlines and x402/MPP are unchanged.
Nayori MCP for existing agents
Bounded job discovery adds optional
nayori_list_jobs for either role. It does not claim jobs or sign, and leaves
the existing six-tool default unchanged. Source constructor createNayoriMcp retains the old
Hermes names as compatibility aliases; use your existing agent and its own configured LLM.
The stable package includes the Node-only nayori-mcp stdio binary for public testnet
reads and offline buyer/provider commitments. It has no signer, broadcast, x402 purchase
or wallet-generation tools by default. Optional operator-configured delegation to the separate
nayori-custody pilot enables bounded QA actions without putting a key in MCP. Both are included
in npm 0.9.0 (since 0.8.0). Clean registry installation and offline checks cover both roles. A prior published
prerelease also passed a supervised funded lifecycle; broader autonomy and x402 remain separate gates.
Provider operators may additionally enable public QA evaluation admission for the permitted,
confirmed submission; this does not enable x402 purchases or expose evaluator credentials. See
MCP setup and custody permissions, limits and release gates.
Role-specific recording/QA guides: consumer and provider.
Connect your existing Hermes, OpenClaw, Codex or Claude Code through MCP client setup. Hermes has a verified internal lifecycle. OpenClaw, Codex and Claude Code passed native MCP connection checks for both roles; Codex also passed context and unsigned preparation calls. These network-disabled probes used no LLM, signer or funds; other client-specific E2Es remain pending. Evaluation admission has a bounded 45-second timeout; status reads retain 15 seconds. Check evaluator capacity before submission and reconcile the existing evaluation ID after an error. Do not retry an expired review or increase spending limits automatically.
Earned-service-fee integration
Mainnet v6/v5 uses a 2% included, evaluation-earned fee: net provider payout on approval or net client refund on evaluated rejection. The full budget stays in escrow until final settlement. Generic testnet remains v5/v4; the QA adapter continues to select v6/v5 explicitly. Existing jobs remain bound to their original contracts. See the integration and accounting guide before enabling any signer.
Runtime
- Node.js 20 or newer
- npm 10 or newer
Install
npm install --save-exact @perkos/[email protected]npm latest resolves to 0.9.0 (published 2026-09-13); dist-tag next still points at the
historical 0.8.0-rc.2 prerelease. Pin the exact version and keep the lockfile in any financial
agent rather than relying on an unqualified install.
To develop from source:
git clone https://github.com/PerkOS-xyz/PerkOS-Nayori-Agent-SDK.git
cd PerkOS-Nayori-Agent-SDK
npm install
npm run verify
npm run quickstartThe quickstart performs public read-only calls and does not require a wallet or private key.
Set PERKOS_NETWORK=testnet to read the testnet deployment.
The x402 foundation example is also read-only and requires no wallet. It creates and round-trips
the official v2 PAYMENT-REQUIRED header for an existing PerkOS escrow job:
npm run quickstart:x402The facilitator example uses public Hiro v3 transaction and event data to inspect a historical mainnet funding transaction. It proves the verifier fails closed when that otherwise matching proof is outside its payment window; it does not write replay state or submit a transaction:
npm run quickstart:x402:facilitatorThe payer quickstart creates a request-bound USDCx intent, reserves explicit spending and fee limits, and builds a canonical unsigned Stacks transaction. It contains no private key, performs no network call, and does not request a wallet signature or broadcast:
npm run quickstart:x402:payerThe MPP PaymentAuth quickstart creates a standard WWW-Authenticate: Payment USDCx challenge,
applies the same payer policy and builds an unsigned OnChainOnly Stacks transaction. It is
offline, contains no private key and does not request a wallet signature or broadcast:
npm run quickstart:mppThe transactional quickstart is safe by default: it prints an offline, role-separated STX/sBTC testnet workflow with committed criteria, provider evidence and appeal-aware settlement.
npm run quickstart:testnetUse the role-separated QA guide and
examples/testnet.env.example. Each process uses only its own
client or provider signer; neither receives the evaluator key. One action runs per invocation.
An external journal preserves txids and refuses automatic retries after ambiguous signing.
The bridge and quickstart ship in 0.9.0 (first included in 0.8.0) for the QA workflow. Registry
installation and offline role checks passed on the 0.8.0 line; job16 separately verified the funded
npm SDK lifecycle with policy0/6 on the historical 0.8.0-rc.2 prerelease.
See the separate evidence scopes.
Read on-chain state
import { PerkOSClient } from "@perkos/agent-sdk";
const perkos = new PerkOSClient({ network: "mainnet" });
const agentCount = await perkos.getAgentCount();
const sbtcJob = await perkos.getJob("sbtc", 1n);
const escrow = await perkos.getEscrowBalance("sbtc", 1n);
const reputation = await perkos.getReputation(
"SP000000000000000000002Q6VF78"
);Amounts are returned as bigint. sBTC values are satoshis and STX values are micro-STX.
Build a transaction plan
Builders return explicit, inspectable plans. They do not sign, broadcast, or read environment variables.
const plan = perkos.transactions.registerAgent({
name: "Research Agent",
description: "Produces cited market research.",
wallet: "SP...",
endpoints: [{ name: "mcp", url: "https://agent.example/mcp" }],
});
console.log(plan.contract, plan.functionName, plan.functionArgs);Funding plans require the sender and generate an exact post-condition:
const plan = perkos.transactions.fundJob({
asset: "sbtc",
jobId: 7n,
amount: 25_000n,
sender: "SP...",
serviceFeeAcceptance: {
gross: 25_000n,
basisPoints: 200,
treasury: "SP1NT1V4X6GQR6T32Z8MSMNECZ6GSWX9HZ81SM1Y8",
rejectionRefund: "net-after-evaluation",
},
});Settlement helpers read the job and escrow balance before building exact contract-principal post-conditions. This protects both wallet-originated and headless transactions from transferring more than the job requires.
Active versioned escrow
Published version 0.9.0 selects agentic-commerce-v6, sbtc-commerce-v5 and
reputation-registry-v3 by default on mainnet. Generic testnet defaults deliberately remain
v5/v4/v3; the QA adapter pins its reviewed v6/v5 contracts explicitly. A new mainnet job therefore
needs no contract override:
import { PerkOSClient } from "@perkos/agent-sdk";
const perkos = new PerkOSClient({
network: "mainnet",
signer,
});
const job = await perkos.getJob("sbtc", 7n);
console.log(job?.submittedAtBurn, job?.reviewDeadline, job?.status);
// The evaluator records an explainable decision without moving escrow.
await perkos.recordDecision({
asset: "sbtc",
jobId: 7n,
decision: "approve",
evidenceHash: evidenceSha256,
explanationHash: explanationSha256,
});
const sync = await perkos.getReputationSync("sbtc", 7n);
if (sync?.pending) await perkos.retryReputationSync("sbtc", 7n);The fixed 12-burn-block review window is readable through getReviewWindow(asset). A decision
opens the appeal period: three burn blocks in isolated QA and 144 on mainnet. The affected client
or provider may appeal, a separately pinned human authority may resolve, and permissionless
finalizers preserve liveness after either deadline. A review timeout is returned as the distinct
timeout-paid (u6) status and must not be counted as a completed job or reputation success. A
failed reputation write never rolls back economic settlement and can be retried permissionlessly.
Autonomous evaluation and appeals
Published 0.9.0 uses agentic-commerce-v6 and sbtc-commerce-v5 by default on
mainnet:
const autonomous = new PerkOSClient({
network: "mainnet",
signer,
});
await autonomous.recordDecision({
asset: "sbtc",
jobId: 7n,
decision: "approve",
evidenceHash: evidenceSha256,
explanationHash: explanationSha256,
});
const decision = await autonomous.getDecision("sbtc", 7n);
console.log(decision?.originalDecision, decision?.appealDeadline);
await autonomous.appealDecision({
asset: "sbtc",
jobId: 7n,
evidenceHash: appealSha256,
});Unappealed decisions are finalized with finalizeDecision. A separate pinned authority uses
resolveAppeal; if it misses the second deadline, settleAppealTimeout preserves the original
decision permissionlessly. High-level settlement methods derive recipients from on-chain state and
use the live escrow plus the job-pinned sBTC token to construct exact deny-mode post-conditions.
Every digest must be a non-zero 32-byte hexadecimal string or Uint8Array.
For sbtc-commerce-v2, sbtc-commerce-v3, sbtc-commerce-v4 and sbtc-commerce-v5, high-level settlement helpers also read the token
pinned when the job was funded. Both the trait argument and exact fungible-token post-condition use
that historical token, so rotating the contract's future funding default cannot strand an existing
escrow.
The previous v5/v4, v4/v3 and v3/v2 generations remain supported through explicit same-network
overrides. Existing jobs must keep their original contract IDs; changing SDK defaults never
migrates escrow.
The historical v4/v3/v3 generation first passed on Stacks testnet. Controlled STX and
official PoX-5 sBTC complete paths pass 27/27 and 30/30. The real timeout path passes preparation
20/20, settlement 12/12 and
separate public-state verification 10/10. Job u2 settled at burn 11290 in
0x06537111…15bb9,
ending in timeout-paid (u6) with zero escrow, one exact 1,000-atomic-unit sBTC payout and no
completion, reputation or rating credit. The frozen evidence is documented in the
contracts/Web repository.
The historical v5/v4 sources are live on mainnet under
SP2K7PV5NXBNRV510S6DCA6RFMTFHAF3ZPK6ZSXPH. Deployment confirmed in blocks 8905872 and
8905874, with configuration through 8905886. Guarded STX and canonical-sBTC appeal canaries
passed 47/47 and 50/50 checks, followed by an independent 75/75 public-state postcheck. Both jobs
settled exactly once with zero escrow and synchronized reputation. Those actors are team-operated
release evidence, not external adoption,
non-team wallet activity or revenue. The independent external security review remains open.
Browser signer (Leather and other Stacks wallets)
StacksConnectSigner adapts the official Stacks Connect request API. The application supplies
the connected address and request function; the SDK forwards the complete plan and never receives
wallet key material.
import { request } from "@stacks/connect";
import { PerkOSClient, StacksConnectSigner } from "@perkos/agent-sdk";
const signer = new StacksConnectSigner({
network: "mainnet",
address: connectedStacksAddress,
request,
});
const perkos = new PerkOSClient({ network: "mainnet", signer });Install @stacks/connect in the browser application. Wallet discovery and account selection stay
at the application boundary so the SDK cannot silently choose an account.
Invite-only partner OAuth and MCP
NayoriPartnerClient enrolls an invited partner without taking custody of a wallet. It requests a
single-use challenge, delegates the exact plaintext to Leather through stx_signMessage, submits
the signature and returns the generated OAuth client secret once. OAuth authorizes API/MCP access
only; every payment remains a separate wallet-signed transaction.
import { request } from "@stacks/connect";
import {
NayoriPartnerClient,
createStacksConnectPartnerSigner,
} from "@perkos/agent-sdk";
const partners = new NayoriPartnerClient();
const credentials = await partners.enroll({
invitationToken,
walletAddress: connectedTestnetAddress,
signMessage: createStacksConnectPartnerSigner(request),
});
// Move the one-time secret directly into the application's secret manager.
const token = await partners.requestToken({
clientId: credentials.clientId,
clientSecret: credentials.clientSecret,
scopes: ["mcp:invoke"],
});
const tools = await partners.callMcp({
accessToken: token.accessToken,
method: "tools/list",
});See Partner pilot for the complete enrollment, scope and secret-handling boundary.
Headless signer
HeadlessSigner requests key material only when deriving the public address or signing. It caches
only the derived address. Use an external secret manager, KMS adapter, or another controlled key
provider; never hardcode production keys.
import { HeadlessSigner, PerkOSClient } from "@perkos/agent-sdk";
const signer = new HeadlessSigner({
network: "testnet",
privateKeyProvider: async () => loadKeyFromSecretManager(),
});
const perkos = new PerkOSClient({ network: "testnet", signer });The callback may return a string or Uint8Array. The SDK copies and zeroes its local binary-key
buffer after use; JavaScript strings cannot be reliably erased from memory.
Confirmation receipts
Broadcast acceptance is not execution success. Confirm through the configured Stacks API before advancing an automated workflow:
const broadcast = await perkos.execute(plan);
const confirmation = await perkos.confirm(broadcast, {
timeoutMs: 10 * 60_000,
pollIntervalMs: 5_000,
});
if (confirmation.status !== "success") {
throw new Error(`Transaction ended with ${confirmation.status}`);
}Confirmation status is normalized to pending, success, abort, dropped, or timeout, with
block and Clarity-result fields when the API supplies them. Polling supports cancellation through
an AbortSignal. If the v3 status response omits a terminal Clarity result, the tracker hydrates
that field from the transaction-detail endpoint.
Spending policy
Funding is fail-closed. Both limits must be configured for every spendable asset:
const perkos = new PerkOSClient({
network: "mainnet",
signer: new WalletSigner(),
spendingPolicy: {
allowedAssets: ["sbtc"],
maxPerTransaction: { sbtc: 50_000n },
maxPerSession: { sbtc: 150_000n },
},
});
const job = await perkos.getJob("sbtc", 7n);
const fee = await perkos.getJobServiceFee("sbtc", 7n);
if (!job) throw new Error("Job not found");
const serviceFeeAcceptance = {
gross: job.budget,
basisPoints: 200 as const,
treasury: fee.treasury,
rejectionRefund: "net-after-evaluation" as const,
};
await perkos.fundJob({
asset: "sbtc",
jobId: 7n,
amount: 25_000n,
serviceFeeAcceptance,
});The policy also limits networks and contract principals. Funding attempts without explicit per-transaction and per-session limits are rejected before the signer is called.
x402 v2 client foundation
The SDK uses the official @x402/core v2 envelope and header codecs while preserving the PerkOS
escrow lifecycle. A protected resource describes an existing, open job and its exact atomic
budget. The scheme client validates that quote against on-chain state, funds it through the
configured SDK signer and spending policy, waits for successful confirmation, and produces the
PAYMENT-SIGNATURE payload.
import { x402Client } from "@x402/core/client";
import {
PerkOSClient,
PerkOSX402SchemeClient,
createPerkOSX402PaymentRequired,
encodePaymentRequiredHeader,
toStacksX402Network,
} from "@perkos/agent-sdk";
const perkos = new PerkOSClient({
network: "mainnet",
signer,
spendingPolicy: {
allowedAssets: ["sbtc"],
maxPerTransaction: { sbtc: 25_000n },
maxPerSession: { sbtc: 50_000n },
},
});
const job = await perkos.getJob("sbtc", 7n);
const fee = await perkos.getJobServiceFee("sbtc", 7n);
if (!job) throw new Error("Job not found");
const required = createPerkOSX402PaymentRequired(perkos.config, {
resource: {
url: "https://agent.example/jobs/7/fund",
description: "Fund job 7 escrow",
mimeType: "application/json",
},
asset: "sbtc",
jobId: 7n,
amount: job.budget,
serviceFeeTerms: {
gross: job.budget,
basisPoints: 200,
treasury: fee.treasury,
rejectionRefund: "net-after-evaluation",
},
});
const network = toStacksX402Network(perkos.config.network);
const paymentClient = new x402Client()
.setSpendControls({
allowedAssets: [
{ network, asset: required.accepts[0].asset, maxAmountPerPayment: "25000" },
],
})
.register(
network,
new PerkOSX402SchemeClient({
client: perkos,
// Replace with an operator UI or deterministic policy that shows and accepts
// the exact gross, 200 bps, treasury and net-rejection terms.
acceptServiceFee: async (terms, requirement) =>
operatorAccepted({ terms, requirement }),
})
);
const paymentPayload = await paymentClient.createPaymentPayload(required);
console.log(encodePaymentRequiredHeader(required), paymentPayload);The proof produced here is client-side confirmation evidence, not authorization by itself. Use the facilitator below to independently inspect the Stacks transaction, match its payment fields, apply a confirmation policy, and consume it through a replay store.
Direct x402 payments on Stacks
The separate stacks-signed-tx-v1 profile covers immediate pay-per-call resources. It uses the
official x402 v2 exact scheme and explicit upfront flow because the facilitator must commit the
signed Stacks transaction before the resource handler runs. It supports:
| Asset | Wire asset | Canonical internal identity | Atomic unit |
|---|---|---|---|
| STX | STX | CAIP-19 slip44:5757 | micro-STX |
| sBTC | canonical address.contract | CAIP-19 SIP-010 | satoshi |
| USDCx | canonical address.contract | CAIP-19 SIP-010 | 10^-6 USDCx |
Create a short-lived request quote and compatibility-first requirement without a wallet or network call:
import {
createNayoriX402PaymentRequirements,
createNayoriX402Quote,
} from "@perkos/agent-sdk";
const quote = await createNayoriX402Quote({
quoteId: "quote-weather-001",
merchantId: "merchant-weather",
network: "testnet",
asset: "usdcx",
amount: 100_000n,
payTo: "ST...",
method: "POST",
url: "https://api.example.com/v1/weather",
body: JSON.stringify({ city: "Miami" }),
issuedAt: Math.floor(Date.now() / 1000),
expiresAt: Math.floor(Date.now() / 1000) + 300,
});
const requirement = await createNayoriX402PaymentRequirements(quote);The quote fingerprint is placed in the 34-byte Stacks/SIP-010 memo, so the origin signature binds
the transaction to the HTTP method, canonical URL, body digest, network, asset, exact amount,
recipient, merchant, and expiry. verifyNayoriX402DirectPayment then validates the x402 envelope,
trusted quote, actual request, canonical transaction encoding, origin signature, payer, network,
memo, transfer template, amount, recipient, token contract, function arguments, deny mode, and
exact post-condition.
The verifier is intentionally pure: it does not authenticate the merchant, validate a quote
signature, read balances/nonces, simulate, persist replay state, sponsor, broadcast, confirm, or
deliver a resource. A hosted facilitator must perform those controls before using the verified
plan. For origin-signed sponsored transactions the result omits transactionId until a sponsor
adds its signature; transactionHash identifies only the supplied serialization.
NayoriX402PaymentClient completes the payer side without weakening those boundaries. It creates
an immutable PaymentIntent, applies recipient/origin/asset and amount/fee limits, constructs the
canonical unsigned transfer, delegates signing to LeatherSigner (broadcast: false) or a
PolicySigner backed by external custody, and verifies the signed result before returning the exact
facilitator request body. See docs/X402_PAYMENTS.md for both integrations.
Direct payments and perkos-escrow-v1 are complementary. Use direct payments for immediate
resources and escrow for jobs that require delivery, evaluation, payout, or reputation.
MPP PaymentAuth with USDCx on Stacks
MPP is a separate standards-based commerce path beside x402. Nayori implements the official
Payment HTTP authentication scheme with method="usdc", intent="charge" and the direct Stacks
USDCx profile. The server issues a canonical WWW-Authenticate: Payment challenge and selects
Payment-Authorization for the payment credential, leaving Authorization: Bearer available for
OAuth.
import {
createNayoriMppUsdcStacksChallenge,
createNayoriMppUsdcStacksCredential,
encodeNayoriMppCredentialHeader,
} from "@perkos/agent-sdk";
const { challenge, wwwAuthenticate } =
await createNayoriMppUsdcStacksChallenge({
quote: trustedUsdcxQuote,
realm: "api.nayori.ai",
description: "Nayori paid agent request",
});
const credential = createNayoriMppUsdcStacksCredential({
challenge,
source: `stacks:2147483648:${payerAddress}`,
transaction: walletSignedTransactionHex,
});
console.log(wwwAuthenticate);
console.log(encodeNayoriMppCredentialHeader(credential));The verifier reuses Nayori's exact request-bound Stacks transaction checks and adds the MPP rules:
RFC 8785 envelopes, CAIP-10 source, canonical official USDCx identity, OnChainOnly, standard
single-signature authorization and low-s signing. Replay persistence, nonce/balance preflight,
broadcast, confirmations, receipt delivery and merchant authentication remain hosted Platform
responsibilities. See MPP payments for the complete flow and trust boundary.
The testnet registry follows Circle xReserve remote domain 10003 and the current canonical token
ST2WK9SGBJ15RHZ33KK0KYVSXBEBHM1XDM8C96EC4.usdcx. Mainnet remains
SP120SBRBQJ00MCWS7TM5R8WJNTTKD5K0HFRC2CNE.usdcx.
x402 v2 Stacks facilitator
PerkOSX402Facilitator implements the official SchemeNetworkFacilitator interface. It reloads
the transaction and events from Hiro, then matches the successful contract call, payer, commerce
contract, decoded job-funded event, exact STX/sBTC transfer, job, amount, token, block metadata,
confirmation depth, and payment-window freshness. Its settle method does not broadcast another
transaction: for the upfront flow it atomically consumes the already-confirmed funding proof.
import { x402Facilitator } from "@x402/core/facilitator";
import {
InMemoryX402ReplayStore,
PerkOSX402Facilitator,
STACKS_X402_NETWORKS,
} from "@perkos/agent-sdk";
const stacks = new PerkOSX402Facilitator({
config: perkos.config,
replayStore: new InMemoryX402ReplayStore(),
minConfirmations: 2,
});
const facilitator = new x402Facilitator().register(
STACKS_X402_NETWORKS.mainnet,
stacks
);The in-memory replay store is only for tests, examples, local development, and a single-process
demo. Production must inject a durable shared implementation whose consume operation is atomic,
such as a database unique constraint or Redis SET NX.
The current Stacks proof is an already-public funding transaction rather than a request-bound off-chain authorization. Freshness and one-time consumption prevent stale reuse, but a party that obtains the proof before settlement could try to consume it first. Do not use this release for a high-value production paywall until request binding is added through a payer signature/challenge or facilitator-submitted signed transaction.
Supported lifecycle
- Register, update, and deactivate agents.
- Read agent records and counts.
- Create STX or sBTC jobs.
- Set a budget and fund escrow.
- Assign a provider and submit a deliverable.
- Complete, reject, or expire a job.
- Settle an overdue review to the provider with exact post-conditions.
- Read and retry durable reputation synchronization.
- Rate a provider and read reputation.
Private QA evidence
NayoriPrivateEvidenceClient implements the bounded private-evidence workflow used by the QA MCP:
provider bytes are hashed locally, uploaded directly to Nayori's private versioned S3 bucket, and
completed as an immutable evidence reference. OAuth is sent only to api.qa.nayori.ai, never S3.
Consumers and the evaluator exchange the stable reference for a fresh, at-most-60-second download
capability and verify the exact byte count and SHA-256 again.
The MCP flag --private-evidence-client /absolute/mode-600.json enables
nayori_private_evidence_upload for the provider and nayori_private_evidence_read for both job
roles. The file is the wallet-linked OAuth client returned during the one-time enrollment; it must
have exactly the role's evidence scopes. It contains no wallet private key. Upload accepts only
inline text/plain or application/json up to 8192 bytes, never filesystem paths. A temporary
503 is surfaced with bounded retry guidance; prepare is not automatically replayed.
This capability is QA/testnet-only until the independent production storage and operational gates are completed. Internal QA activity is not external M2 adoption.
Security model
- Mainnet or testnet must be selected explicitly.
- Contract overrides must match the selected network.
- Funding uses exact sender post-conditions.
- Settlement uses exact escrow-contract post-conditions.
- Agent-controlled payments require transaction and session budgets.
- The core SDK never accepts arbitrary URLs or payment destinations from an LLM.
executerefuses plans created for a different network or signer.- Broadcast receipts require a valid 32-byte Stacks transaction ID.
- Automated workflows can wait for an explicit terminal confirmation before their next action.
- x402 quotes are bound to the configured network, escrow contract, asset, job, and exact budget.
- x402 settlement verifies current chain evidence and atomically consumes each funding txid once.
- MPP USDCx credentials echo a request-bound challenge and require a canonical, low-s,
OnChainOnlyStacks transfer; hosted settlement must still consume challenge and replay keys atomically before broadcast. - Partner OAuth enrollment signs an exact, short-lived, invitation-bound message in the wallet; the SDK normalizes the public signature but never receives a private key.
- OAuth client secrets and access tokens are application secrets. The SDK returns them to the caller but does not persist, log or refresh them automatically.
The independent external security review remains open. Do not treat this release as audited software.
See Architecture, x402 payments, MPP payments, Partner pilot and Security for the trust boundaries and responsible disclosure process.
QA promotion
New integration work is reviewed in pull requests targeting qa; promotion to main is a
separate reviewed release after QA verification. QA consumes an exact commit or tarball and
never publishes npm as a side effect. Operator deployments are initiated from the approved Mac,
with image builds on the VPS; do not assume GitHub-hosted runners can reach that server.
The repository also retains the historical Promote exact main commit to QA workflow. It accepts
only a full SHA already in main and a fast-forward from qa. It is not the forward path for
new QA-only changes: do not force or rewind branches to make it run. Branch protection and
restricted GitHub Actions bypass remain required where that legacy workflow is used.
Development
npm install
npm run typecheck
npm test
npm run buildLicense
MIT
