hedera-curb 0.4.1 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -21
- package/dist/cli.js +21 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# hedera-curb
|
|
2
2
|
|
|
3
|
-
**Verifiable spend-control for Hedera AI agents.** Drop-in [Hedera Agent Kit](https://github.com/hashgraph/hedera-agent-kit-js) hooks that govern every payment your agent makes — per-task & rolling-24h budgets
|
|
3
|
+
**Verifiable spend-control for Hedera AI agents.** Drop-in [Hedera Agent Kit](https://github.com/hashgraph/hedera-agent-kit-js) hooks that govern every payment your agent makes — per-task & rolling-24h **budgets**, a counterparty **allowlist**, and single-use **human approval** — and write every decision immutably to the **Hedera Consensus Service**.
|
|
4
4
|
|
|
5
|
-
>
|
|
5
|
+
> Agents can pay now. `hedera-curb` makes sure they can't overspend, pay a stranger, or move money without a record.
|
|
6
|
+
|
|
7
|
+
**[🌐 Live demo](https://hedera-curb.vercel.app) · [📖 Docs](https://hedera-curb.vercel.app/docs) · [📚 API reference](https://hedera-curb.vercel.app/docs#api) · [💻 GitHub](https://github.com/Madhav-Gupta-28/Curb)**
|
|
8
|
+
|
|
9
|
+
---
|
|
6
10
|
|
|
7
11
|
## Install
|
|
8
12
|
|
|
@@ -15,38 +19,93 @@ npm i hedera-curb @hashgraph/hedera-agent-kit @hiero-ledger/sdk
|
|
|
15
19
|
```ts
|
|
16
20
|
import { createCurb } from 'hedera-curb';
|
|
17
21
|
|
|
18
|
-
const curb = await createCurb({ client, agentAccountId });
|
|
22
|
+
const curb = await createCurb({ client, agentAccountId });
|
|
19
23
|
new HederaAIToolkit({ client, configuration: { context: { hooks: curb.hooks } } });
|
|
20
24
|
```
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
That's it — every governed transfer your agent attempts now passes the budget, allowlist, and approval policies **before** it executes, and every decision lands on your Hedera audit topic.
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
agent tries to pay
|
|
30
|
+
│
|
|
31
|
+
▼
|
|
32
|
+
┌─────────────────────────────────────────────┐
|
|
33
|
+
│ curb.hooks │
|
|
34
|
+
│ 1. allowlist → not vetted? ✗ block │
|
|
35
|
+
│ 2. spend limits → over a cap? ✗ block │
|
|
36
|
+
│ 3. approval tier → large? ⏸ human │
|
|
37
|
+
└─────────────────────────────────────────────┘
|
|
38
|
+
│ │
|
|
39
|
+
✓ allowed every decision
|
|
40
|
+
▼ ▼
|
|
41
|
+
payment executes 📜 HCS audit topic
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## `createCurb(opts)`
|
|
45
|
+
|
|
46
|
+
The one-call setup. It auto-provisions the audit topic + an HCS-2 policy registry, defaults the store, publishes your starting policy, and returns ready-to-use hooks.
|
|
47
|
+
|
|
48
|
+
**What you pass:**
|
|
49
|
+
|
|
50
|
+
| Option | Type | Default | |
|
|
51
|
+
| --- | --- | --- | --- |
|
|
52
|
+
| `client` | `Client` | **required** | A `@hiero-ledger/sdk` operator client |
|
|
53
|
+
| `agentAccountId` | `string` | **required** | The account the agent pays from |
|
|
54
|
+
| `perTask` | `number` | `10` | Max value of a single payment |
|
|
55
|
+
| `perDay` | `number` | `25` | Rolling-24h cap |
|
|
56
|
+
| `autoUnder` | `number` | `3` | Below this, payments auto-approve |
|
|
57
|
+
| `approveUnder` | `number` | `10` | At/above this, an approval is flagged high-value |
|
|
58
|
+
| `currency` | `'HBAR' \| 'USDC'` | `'HBAR'` | Display currency |
|
|
59
|
+
| `store` | `CurbStore` | in-memory | Bring your own (Redis/Postgres) for production |
|
|
60
|
+
| `auditTopicId` | `string` | auto-created | Reuse an existing HCS topic |
|
|
61
|
+
| `policyTopicId` | `string \| false` | auto-created | Reuse a registry, or `false` to skip versioning |
|
|
62
|
+
|
|
63
|
+
**What you get back:**
|
|
23
64
|
|
|
24
65
|
```ts
|
|
25
|
-
|
|
66
|
+
const { hooks, config, store, auditTopicId, policyTopicId } = curb;
|
|
67
|
+
// └ drop into the kit └ resolved caps └ for /verify └ HCS-2 registry
|
|
26
68
|
```
|
|
27
69
|
|
|
28
|
-
|
|
70
|
+
Override anything inline:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
await createCurb({ client, agentAccountId, perDay: 25, autoUnder: 3, store: myRedisStore });
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Provision from the terminal (no code)
|
|
77
|
+
|
|
78
|
+
Creates the topics and prints your `.env` — handy for CI or a one-time setup:
|
|
29
79
|
|
|
30
80
|
```bash
|
|
31
81
|
npx hedera-curb init --network testnet
|
|
32
|
-
# ✓ Audit topic:
|
|
82
|
+
# ✓ Audit topic: 0.0.x
|
|
83
|
+
# ✓ Policy topic: 0.0.y (HCS-2, owner-only writes)
|
|
33
84
|
```
|
|
34
85
|
|
|
35
|
-
|
|
86
|
+
## Advanced — wire it yourself
|
|
87
|
+
|
|
88
|
+
Prefer to assemble the pieces? `buildCurbHooks` returns the ordered policy + audit stack:
|
|
36
89
|
|
|
37
90
|
```ts
|
|
38
91
|
import { buildCurbHooks, InMemoryCurbStore } from 'hedera-curb';
|
|
92
|
+
|
|
39
93
|
const store = new InMemoryCurbStore();
|
|
40
94
|
store.allowAccount(AGENT_ID, PROVIDER_ID);
|
|
41
|
-
const cfg = { agentAccountId: AGENT_ID, auditTopicId: TOPIC_ID, currency: 'HBAR' as const, perTask: 10, perDay: 25, autoUnder: 3, approveUnder: 10 };
|
|
42
|
-
// context: { hooks: buildCurbHooks({ cfg, store }) }
|
|
43
|
-
```
|
|
44
95
|
|
|
45
|
-
|
|
96
|
+
const cfg = {
|
|
97
|
+
agentAccountId: AGENT_ID,
|
|
98
|
+
auditTopicId: TOPIC_ID,
|
|
99
|
+
currency: 'HBAR' as const,
|
|
100
|
+
perTask: 10, perDay: 25, autoUnder: 3, approveUnder: 10,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// configuration: { context: { hooks: buildCurbHooks({ cfg, store }) } }
|
|
104
|
+
```
|
|
46
105
|
|
|
47
106
|
## Production storage
|
|
48
107
|
|
|
49
|
-
`InMemoryCurbStore` is for tests
|
|
108
|
+
`InMemoryCurbStore` is perfect for tests and single-instance apps. For production (multi-instance / serverless), implement the `CurbStore` interface against Redis, Postgres, etc. Spend is a rolling-24h window split into **committed** (executed) spend and short-lived **holds** (atomic reservations), so two concurrent payments can't both slip under the cap:
|
|
50
109
|
|
|
51
110
|
```ts
|
|
52
111
|
interface CurbStore {
|
|
@@ -61,17 +120,22 @@ interface CurbStore {
|
|
|
61
120
|
}
|
|
62
121
|
```
|
|
63
122
|
|
|
64
|
-
##
|
|
123
|
+
## Exports
|
|
65
124
|
|
|
66
|
-
|
|
125
|
+
| | |
|
|
126
|
+
| --- | --- |
|
|
127
|
+
| `createCurb(opts)` | One-call setup — topics, store, hooks |
|
|
128
|
+
| `buildCurbHooks(deps)` | The ordered policy + audit stack for `context.hooks` |
|
|
129
|
+
| `SpendLimitPolicy`, `CounterpartyAllowlistPolicy`, `ApprovalTierPolicy`, `CurbAuditHook` | The individual policies, composable on their own |
|
|
130
|
+
| `createAuditTopic`, `createPolicyRegistry`, `publishPolicyVersion`, `readPolicyVersions`, `currentPolicy` | HCS-2 policy versioning |
|
|
131
|
+
| `InMemoryCurbStore`, `extractPayment`, `paymentKey`, `writeRecord` | Store + helpers |
|
|
132
|
+
| `CurbStore`, `CurbConfig`, `CurbRecord`, `PolicyVersion`, … | Types |
|
|
67
133
|
|
|
68
|
-
|
|
134
|
+
**Every export is documented with parameters, types, and examples → [hedera-curb.vercel.app/docs](https://hedera-curb.vercel.app/docs)**
|
|
135
|
+
|
|
136
|
+
## Beyond budgets — the trust ladder
|
|
69
137
|
|
|
70
|
-
-
|
|
71
|
-
- `buildCurbHooks(deps)` — the ordered policy + audit stack for `context.hooks`.
|
|
72
|
-
- `SpendLimitPolicy`, `CounterpartyAllowlistPolicy`, `ApprovalTierPolicy`, `CurbAuditHook`.
|
|
73
|
-
- `createAuditTopic`, `createPolicyRegistry`, `publishPolicyVersion`, `readPolicyVersions`, `currentPolicy` — HCS-2 policy versioning.
|
|
74
|
-
- `CurbStore` + `InMemoryCurbStore`, `extractPayment`, `paymentKey`, `writeRecord`, and the `CurbConfig` / `CurbRecord` types.
|
|
138
|
+
This package is **L0** of a [larger trust ladder](https://github.com/Madhav-Gupta-28/Curb): off-chain hooks (here) → **L1** an HCS-2 versioned policy registry + a `/verify` that recomputes spend from the on-chain log → **L2** an on-chain **`CurbVault`** contract that enforces the caps + allowlist *in Hedera consensus*, so even a fully-compromised agent key can't overspend. Plus fully non-custodial flows (HIP-336 allowances + HIP-423 scheduled approvals). Pick how much to trust the server.
|
|
75
139
|
|
|
76
140
|
## License
|
|
77
141
|
|
package/dist/cli.js
CHANGED
|
@@ -6,9 +6,24 @@ import { createAuditTopic } from './audit.js';
|
|
|
6
6
|
import { createPolicyRegistry, publishPolicyVersion } from './policy-registry.js';
|
|
7
7
|
import { DEFAULT_CONFIG } from './config.js';
|
|
8
8
|
const flag = (name) => {
|
|
9
|
-
const
|
|
9
|
+
const eq = process.argv.find((a) => a.startsWith(`--${name}=`)); // support --name=value
|
|
10
|
+
if (eq)
|
|
11
|
+
return eq.slice(name.length + 3);
|
|
12
|
+
const i = process.argv.indexOf(`--${name}`); // and --name value
|
|
10
13
|
return i >= 0 ? process.argv[i + 1] : undefined;
|
|
11
14
|
};
|
|
15
|
+
// a numeric flag must parse to a positive, finite number — otherwise we'd publish a garbage policy (NaN caps).
|
|
16
|
+
const num = (name, def) => {
|
|
17
|
+
const raw = flag(name);
|
|
18
|
+
if (raw === undefined)
|
|
19
|
+
return def;
|
|
20
|
+
const n = Number(raw);
|
|
21
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
22
|
+
console.error(`Invalid --${name}: "${raw}" (expected a positive number).`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
return n;
|
|
26
|
+
};
|
|
12
27
|
function parseKey(s) {
|
|
13
28
|
for (const parse of [PrivateKey.fromStringECDSA, PrivateKey.fromStringED25519, PrivateKey.fromStringDer]) {
|
|
14
29
|
try {
|
|
@@ -29,6 +44,9 @@ async function init() {
|
|
|
29
44
|
console.error('Missing credentials. Pass --account and --key, or set HEDERA_OPERATOR_ID / HEDERA_OPERATOR_KEY.');
|
|
30
45
|
process.exit(1);
|
|
31
46
|
}
|
|
47
|
+
// validate all input (flags + key) BEFORE any on-chain work, so bad input never creates topics
|
|
48
|
+
const perTask = num('per-task', DEFAULT_CONFIG.perTask);
|
|
49
|
+
const perDay = num('per-day', DEFAULT_CONFIG.perDay);
|
|
32
50
|
console.log(`Provisioning Curb on ${network}…`);
|
|
33
51
|
const client = Client.forName(network).setOperator(account, parseKey(key));
|
|
34
52
|
const auditTopicId = await createAuditTopic(client);
|
|
@@ -37,8 +55,8 @@ async function init() {
|
|
|
37
55
|
agentAccountId: agent,
|
|
38
56
|
auditTopicId,
|
|
39
57
|
...DEFAULT_CONFIG,
|
|
40
|
-
perTask
|
|
41
|
-
perDay
|
|
58
|
+
perTask,
|
|
59
|
+
perDay,
|
|
42
60
|
};
|
|
43
61
|
await publishPolicyVersion(client, config, policyTopicId, 'initial policy').catch(() => { });
|
|
44
62
|
console.log(`\n✓ Audit topic: ${auditTopicId}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hedera-curb",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "Verifiable spend-control policies for Hedera AI agents — drop-in Hedera Agent Kit hooks for budgets, allowlists, human approval, and an immutable HCS audit trail.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|