# Loop Agent SDK
[](https://crates.io/crates/loop-agent-sdk)
[](https://docs.rs/loop-agent-sdk)
[](https://opensource.org/licenses/MIT)
Trustless agent infrastructure for the Loop Protocol. Build AI agents that capture value, stake Cred, and optimize yield — without ever touching user funds.
Part of the [loop-protocol](https://github.com/OAR-Technologies-Inc/loop-protocol) monorepo. For the TypeScript SDK, see [`sdk/`](../sdk/).
## Installation
```toml
[dependencies]
loop-agent-sdk = "0.1"
```
MSRV: Rust 1.81.
## Architecture
```
┌─────────────────────────────────────────────────────────┐
│ LOOP AGENT SDK │
├─────────────────┬─────────────────┬─────────────────────┤
│ STATE │ ACTION │ PERCEPTION │
│ │ │ │
│ context_fetch │ capture_value │ on_transaction │
│ context_push │ stake_cred │ on_location_hit │
│ agent_memory │ claim_yield │ on_proof_submitted │
│ │ optimize_yield │ on_position_unlock │
└─────────────────┴─────────────────┴─────────────────────┘
```
## Security Model
**Intent-Based Execution**: Agents don't move funds. They request actions, and smart contracts execute with enforced rules.
- **Session Keys**: Scoped permissions (read/capture/stake) with TTL
- **Trustless Distribution**: 80/14/6 split enforced by contract
- **No Key Access**: Agents never hold private keys
```rust
// Agent requests capture, contract enforces rules
vault.capture_value(&session, merchant_id, proof)?;
// Contract automatically splits: 80% user, 14% treasury, 6% stakers
```
## MCP Compliance
This SDK is [Model Context Protocol](https://modelcontextprotocol.io) compliant. Any MCP-compatible AI can interact with Loop vaults:
```json
// From mcp/loop-vault-manifest.json
{
"tools": [
{ "name": "capture_value", "description": "Submit purchase proof..." },
{ "name": "stake_cred", "description": "Stake for yield..." },
{ "name": "get_vault_status", "description": "Check balances..." }
]
}
```
A user can tell Claude or Gemini: *"How's my Loop Vault doing?"* and the AI knows exactly how to fetch the data.
## Quick Start
```rust
use loop_agent_sdk::{
action::{VaultAction, SessionKey, ZkProof},
perception::{PerceptionEvent, TransactionEvent},
state::{StateStore, UserContext},
};
async fn handle_pos_transaction(tx: TransactionEvent) -> Result<(), Error> {
// 1. Load user context (<100ms target)
let ctx = state_store.context_fetch(&find_user_by_card(&tx.card_fingerprint)?)?;
// 2. Get scoped session key
let session = get_session_key(&ctx.pubkey, &[PermissionScope::Capture]).await?;
// 3. Build proof from POS data
let proof = ZkProof {
proof_type: ProofType::SquarePos,
data: tx.raw_payload.to_vec(),
timestamp: tx.occurred_at,
amount_cents: tx.amount_cents,
card_fingerprint: Some(tx.card_fingerprint),
};
// 4. Capture value (contract enforces 80/14/6 split)
let result = vault.capture_value(&session, tx.merchant_id.parse()?, proof)?;
println!("Minted {} Cred, {} to user",
result.cred_minted,
result.user_amount
);
Ok(())
}
```
## Feature Flags
The SDK is split into composable feature flags so a deployment only pulls in what it needs. The `default` feature enables the full core (`action` + `perception` + `state`); opt into the runtime-specific features on top.
| `default` = `full` | core SDK: action, perception, state | (pure-Rust) |
| `action` | `vault.capture_value`, `stake_cred`, yield actions | — |
| `perception` | transaction / location / proof event handling | — |
| `state` | context fetch/push, agent memory | — |
| `lambda` | AWS Lambda runtime integration | `lambda_runtime`, `tokio` |
| `dynamodb` | DynamoDB state backend | `aws-sdk-dynamodb`, `aws-config`, `tokio` |
| `privacy` | HMAC fingerprinting + zeroize for sensitive data | `hmac`, `zeroize`, `rand`, `aws-sdk-secretsmanager` |
| `webhook` | Fidel / Square / Stripe webhook handlers (implies `privacy`) | `hmac` |
| `notifications` | Supabase push notifications (implies `dynamodb`) | `reqwest` |
| `supabase` | reputation attestation integration | `reqwest`, `tokio` |
| `agent` | the full agent stack: `lambda` + `dynamodb` + `privacy` + `webhook` + `notifications` + `supabase` | all of the above |
| `cli` | `loop-cli` binary | `clap` |
The `loop-agent` binary requires the `agent` feature; the `loop-cli` binary requires the `cli` feature. The `lambda_handler` example requires `lambda + dynamodb`.
`docs.rs` builds with `all-features = true`, so the published documentation reflects every surface.
## AWS Lambda Deployment
```toml
[dependencies]
loop-agent-sdk = { version = "0.1", features = ["lambda", "dynamodb"] }
```
```rust
use lambda_runtime::{service_fn, LambdaEvent, Error};
use loop_agent_sdk::perception::PerceptionEvent;
#[tokio::main]
async fn main() -> Result<(), Error> {
lambda_runtime::run(service_fn(handler)).await
}
async fn handler(event: LambdaEvent<PerceptionEvent>) -> Result<(), Error> {
let (event, _context) = event.into_parts();
match event {
PerceptionEvent::TransactionDetected(tx) => handle_transaction(tx).await,
PerceptionEvent::LocationHit(loc) => handle_location(loc).await,
PerceptionEvent::PositionUnlocking(unlock) => handle_unlock(unlock).await,
_ => Ok(()),
}
}
```
## EventBridge Integration
The SDK generates EventBridge rules for you:
```rust
use loop_agent_sdk::perception::generate_eventbridge_rules;
let rules = generate_eventbridge_rules("arn:aws:lambda:us-east-1:123456:function:loop-agent");
// Deploy rules to EventBridge
```
Events trigger your Lambda:
- `loop.pos/TransactionDetected` → POS webhook
- `loop.mobile/LocationHit` → User enters geofence
- `loop.staking/PositionUnlocking` → 24h before unlock
## Distribution Policy
Every capture follows this split (enforced on-chain):
| User | 80% | Their reward vault |
| Treasury | 14% | Protocol sustainability |
| Stakers | 6% | Yield for staked positions |
## Yield Rates
| 30 days | 8% |
| 90 days | 12% |
| 180 days | 16% |
| 365 days | 20% |
## DynamoDB State Store
The SDK uses DynamoDB for high-speed state persistence (<100ms context reload, <5ms fingerprint lookup).
### Table Schema
```
Table: loop-agent-state
Primary Key: pk (String), sk (String)
GSI: CardFingerprintIndex (fingerprint → user_pubkey)
TTL: Enabled on 'ttl' attribute
Item Types:
USER#{pubkey} | PENDING#{ts} → Pending captures (ring buffer, max 10)
USER#{pubkey} | LOCATION → Current merchant (geofence)
CARD#{fp} | META → Card fingerprint → user mapping
TXN#{id} | PROCESSED → Transaction dedup (30 day TTL)
```
### Setup
```bash
# Create table with GSI and TTL
./scripts/create-dynamodb-table.sh
# Or use Rust
use loop_agent_sdk::dynamo::{create_table, DynamoConfig};
create_table(&client, &DynamoConfig::default()).await?;
```
### Environment Variables
```bash
DYNAMO_TABLE=loop-agent-state # Table name
DYNAMO_FINGERPRINT_GSI=CardFingerprintIndex # GSI name
MAX_PENDING_CAPTURES=10 # Ring buffer limit
SESSION_TTL_SECONDS=86400 # 24 hours
PENDING_TTL_SECONDS=1814400 # 21 days
DAX_ENDPOINT= # Optional DAX cluster
```
### Usage
```rust
use loop_agent_sdk::{DynamoStateStore, PendingCapture};
// Initialize
let store = DynamoStateStore::new().await?;
// Card fingerprint lookup (<5ms)
let user = store.lookup_user_by_card("fp_abc123").await?;
// User context (<100ms with consistent reads)
let ctx = store.context_fetch_async(&user_pubkey).await?;
// Mark transaction processed (dedup)
store.mark_transaction_processed(&txn_id, &user_pubkey, amount).await?;
// Session key with TTL
store.store_session_key(&user_pubkey, &session_data).await?;
// Pending captures (ring buffer)
store.add_pending_capture(&user_pubkey, &capture).await?;
```
## License
MIT - OAR Technologies Inc