# @rubric-protocol/sdk

Post-quantum AI attestation for Node.js. Every AI decision your system makes — signed locally in microseconds, anchored to Hedera's public ledger in the background.

Built for [EU AI Act Article 12](https://rubric-protocol.com) compliance and beyond.

---


## ⚠️ Critical: Endpoint routing by tier

| Endpoint | Tier Required | Behavior | Cost |
|----------|--------------|----------|------|
| `/v1/attest` | **Enterprise only** | Direct HCS write per call | HBAR per call |
| `/v1/standard-attest` | **Standard+** | Batched HCS writes (10s window, 1 HCS write per batch) | Minimal |
| `/v1/tiered-attest` | Developer+ | Merkle batching (1,000,000:1 compression) | Minimal |

**Default for most workloads: `/v1/tiered-attest` (Developer) or `/v1/standard-attest` (Standard).**
Hard rate limit on `/v1/attest`: 60 req/min. If you are unsure which to use: use `/v1/tiered-attest`.

## How it works

The SDK signs AI decisions locally using ML-DSA-65 (NIST FIPS 204, post-quantum) before any network call is made. Attestations are queued and flushed to Rubric's global federation in the background. Your AI pipeline sees zero added latency.

```
AI decision ‒ local sign (<1ms) → proof returned immediately
                                 ↓ background
                          Rubric anchor (5-10s) → HCS confirmed (~30s)
```

Each proof upgrades automatically as anchoring completes. You can fire-and-forget, or await full HCS confirmation for high-stakes decisions.

---

## Install

```bash
npm install @rubric-protocol/sdk
```

Peer dependencies (install only what you use):

```bash
npm install openai              # for OpenAI plugin
npm install @langchain/core     # for LangChain plugin
```

---

## Quickstart

```typescript
import { createRubricClient } from '@rubric-protocol/sdk';

const rubric = createRubricClient({
  apiKey: process.env.RUBRIC_API_KEY!,
  localSigning: true,       // sign locally before network
  backgroundQueue: true,    // non-blocking flush
  node: 'auto',             // route to nearest healthy node
});

const proof = await rubric.attest({
  agentId: 'my-agent-v1',
  output: 'Loan application approved. Score: 742, DTI: 28%.',
  leafType: 'AGENT_OUTPUT',
  metadata: { model: 'gpt-4o', pipeline: 'credit-decisioning' },
});

console.log(proof.attestationId);  // immediate
console.log(proof.stage);          // 'local'

// Optional: wait for full HCS confirmation
proof.onUpgrade('confirmed', (confirmed) => {
  console.log(confirmed.hashScanUrl); // publicly verifiable on HashScan
});
```

---

## LangChain

Add one handler and every LLM call, agent action, chain, and tool invocation is automatically attested.

```typescript
import { ChatOpenAI } from '@langchain/openai';
import { AgentExecutor } from 'langchain/agents';
import { RubricLangChainHandler } from '@rubric-protocol/sdk';

const rubric = new RubricLangChainHandler({
  apiKey: process.env.RUBRIC_API_KEY!,
  localSigning: true,
  backgroundQueue: true,
  events: ['llm', 'agent', 'tool'],  // choose what to attest
  pipelineId: 'my-pipeline',
});

const executor = await AgentExecutor.fromAgentAndTools({
  agent,
  tools,
  callbacks: [rubric],  // that's it
});

await executor.invoke({ input: 'Analyze this transaction for fraud.' });
await rubric.shutdown(); // flush remaining queue on exit
```

---

## OpenAI

Drop-in wrapper — your existing code is unchanged.

```typescript
import OpenAI from 'openai';
import { withRubric } from '@rubric-protocol/sdk';

const openai = withRubric(new OpenAI(), {
  apiKey: process.env.RUBRIC_API_KEY!,
  agentId: 'my-openai-agent',
  localSigning: true,
  backgroundQueue: true,
});

// Use exactly as before — attestation happens automatically
const completion = await openai.chat.completions.create({
  model: 'gpt-4o',
  messages: [{ role: 'user', content: 'Should we approve this claim?' }],
});
```

---

## High-stakes decisions

Developer tier uses Merkle batching. For per-attestation HCS confirmation use Enterprise tier.

**Developer tier — listen for batch confirmation:**
```typescript
const proof = await rubric.attest({
  agentId: 'triage-agent',
  output: 'Patient flagged for immediate review.',
  leafType: 'AGENT_OUTPUT',
});
proof.onUpgrade('confirmed', (p) => {
  console.log(p.hashScanUrl);
});
```

**Enterprise tier — direct per-call HCS anchoring:**
```typescript
const confirmed = await rubric.attestAndConfirm({
  agentId: 'triage-agent',
  output: 'Patient flagged for immediate review.',
  leafType: 'AGENT_OUTPUT',
}, 90_000);
console.log(confirmed.hcsSequenceNumber);
console.log(confirmed.hashScanUrl);
```

Enterprise: [rubric-protocol.com/pricing](https://rubric-protocol.com/pricing)

---

## Proof lifecycle

Every attestation returns a `LiveProof` that upgrades automatically:

| Stage | When | What you have |
|---|---|---|
| `local` | <1ms | ML-DSA-65 signature + timestamp |
| `anchored` | 5–10s | Merkle root committed to Rubric |
| `confirmed` | ~30s | HCS sequence number, HashScan URL |

```typescript
proof.onUpgrade('anchored', (p) => console.log(p.merkleRoot));
proof.onUpgrade('confirmed', (p) => console.log(p.hashScanUrl));
proof.onUpgrade('any', (p) => console.log(p.stage)); // fires on each upgrade
```

---

## Configuration

```typescript
createRubricClient({
  apiKey: string,               // required — get one at rubric-protocol.com
  node?: 'us'|'sg'|'jp'|'ca'|'eu'|'auto',  // default: 'us'
  localSigning?: boolean,       // default: false
  keystorePath?: string,        // default: ~/.rubric/sdk-keypair.json
  keystorePassphrase?: string,  // AES-256-GCM encrypts the keystore
  backgroundQueue?: boolean,    // default: false
  tier?: 'developer' | 'standard' | 'enterprise', // default: developer
  enterprise?: boolean,         // legacy alias for tier: 'enterprise'
  proofUpgrade?: boolean,       // auto-poll for stage upgrades
  timeout?: number,             // HTTP timeout ms, default: 15000
})
```

---

## Nodes

The SDK routes to Rubric's global federation automatically when `node: 'auto'`.

| Region | Endpoint |
|---|---|
| US East | `https://rubric-protocol.com` |
| Singapore | `https://sg.rubric-protocol.com` |
| Japan | `https://jp.rubric-protocol.com` |
| Canada | `https://ca.rubric-protocol.com` |
| EU Central | `https://eu.rubric-protocol.com` |

---

## Security

- **ML-DSA-65** (NIST FIPS 204) — post-quantum signature scheme, same algorithm used server-side
- Keypairs stored at `~/.rubric/sdk-keypair.json` with optional AES-256-GCM encryption via passphrase
- Canonical JSON serialization ensures deterministic, tamper-evident signing
- All attestations anchored to [Hedera Consensus Service](https://hedera.com) — public, immutable, independently verifiable

---

## Requirements

- Node.js >= 18.0.0
- TypeScript >= 5.0 (if using TypeScript)

---

## Links

- [Documentation](https://rubric-protocol.com)
- [API Reference](https://rubric-protocol.com/docs)
- [HashScan](https://hashscan.io/mainnet/topic/0.0.10416909) — live attestation stream
- [EU AI Act Article 12](https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32024R1689)

---

## License

MIT — Echelon Intelligence Systems LLC

*Patent Pending*

## Agent Payment Attestation (attestBeforeSpend)

Wraps a payment tool call so the decision is attested and anchored before money
moves. In enforce mode the payment does not execute unless Rubric acknowledges
the record.

    const { attestBeforeSpend, verifySpendCommitment } = require('@rubric-protocol/sdk');

    const pay = attestBeforeSpend(myPaymentFn, {
      apiKey: process.env.RUBRIC_API_KEY,
      agentId: 'agent-01',
      mandateRef: 'mandate-abc',
      rail: 'x402',
      mode: 'enforce',
      attestReceipt: true,
    });

    const out = await pay(params, { intent: 'supplier invoice', amount: '25.00', currency: 'USDC' });

Retain out.decisionRecord and out.decisionPayloadKey. Rubric stores only a
salted commitment and does not retain the key; without both, the anchored
record cannot be opened.

    verifySpendCommitment(out.decisionRecord, out.decisionPayloadKey, out.decisionCommitment);

See docs/apa-v1-binding-profile.md for the normative specification.
