Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 22 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ StellarSearch is a pay-per-query web search API for autonomous AI agents. Every

## Real stack (no mocks)

| Layer | Real package / service |
|---|---|
| Payment protocol | `@x402/express` + `@x402/stellar` + `@x402/core` |
| Blockchain | Stellar Testnet (via Horizon API) |
| Facilitator | OpenZeppelin x402 (`channels.openzeppelin.com`) |
| Wallet connect | `@stellar/freighter-api` (real Freighter extension) |
| Balances / tx | Stellar Horizon REST API (live, not mocked) |
| Search results | Serper.dev API (real Google search results) |
| AI assistant | `groq-sdk` · Llama 3.3 70B (real Groq API) |
| Frontend | React 18, TypeScript, Tailwind CSS, Framer Motion |
| Layer | Real package / service |
| ---------------- | --------------------------------------------------- |
| Payment protocol | `@x402/express` + `@x402/stellar` + `@x402/core` |
| Blockchain | Stellar Testnet (via Horizon API) |
| Facilitator | OpenZeppelin x402 (`channels.openzeppelin.com`) |
| Wallet connect | `@stellar/freighter-api` (real Freighter extension) |
| Balances / tx | Stellar Horizon REST API (live, not mocked) |
| Search results | Serper.dev API (real Google search results) |
| AI assistant | `groq-sdk` · Llama 3.3 70B (real Groq API) |
| Frontend | React 18, TypeScript, Tailwind CSS, Framer Motion |

---

Expand All @@ -40,11 +40,11 @@ npm install

### 2. Get your keys (all free)

| Key | Where to get it |
|---|---|
| Key | Where to get it |
| --------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `STELLAR_RECEIVING_ADDRESS` | [Stellar Lab](https://laboratory.stellar.org/#account-creator?network=test) — generate + fund testnet keypair |
| `SERPER_API_KEY` | [serper.dev](https://serper.dev/) — free tier: 2.5k queries/month |
| `GROQ_API_KEY` | [console.groq.com/keys](https://console.groq.com/keys) — free |
| `SERPER_API_KEY` | [serper.dev](https://serper.dev/) — free tier: 2.5k queries/month |
| `GROQ_API_KEY` | [console.groq.com/keys](https://console.groq.com/keys) — free |

### 3. Configure

Expand Down Expand Up @@ -141,6 +141,7 @@ Browser (Freighter) → GET /search?q=...
### Payment Integrity & Replay Protection

To guarantee that each payment identifier authorizes **exactly one provider call**, StellarSearch tracks consumed payment identifiers across Express (`server/index.ts`) and Vercel (`api/search.ts`) runtimes:

- **Payload Invalidation:** Extracts transaction hashes (or SHA-256 fallback hashes of payment headers) and invalidates consumed payloads for a 300-second window.
- **Concurrency Throttling:** Rapid parallel requests using identical payment payloads are throttled so only one search query proceeds; concurrent duplicates immediately receive HTTP 402 (`Payment payload already consumed`).

Expand Down Expand Up @@ -498,10 +499,10 @@ The `supply-chain` CI job generates a **CycloneDX SBOM** from the committed lock

## Hackathon requirements

| Requirement | ✓ |
|---|---|
| Open-source repo + README | ✅ |
| 2–3 min video demo | Record showing: connect Freighter → search → see 402 → payment settles → results |
| Real Stellar testnet transactions | ✅ Every search settles 0.001 USDC via OpenZeppelin facilitator |
| x402 protocol | ✅ `@x402/express` + `@x402/stellar` |
| Addresses explicit demand signal | ✅ "pay-per-query web search instead of monthly subscriptions" |
| Requirement | ✓ |
| --------------------------------- | -------------------------------------------------------------------------------- |
| Open-source repo + README | ✅ |
| 2–3 min video demo | Record showing: connect Freighter → search → see 402 → payment settles → results |
| Real Stellar testnet transactions | ✅ Every search settles 0.001 USDC via OpenZeppelin facilitator |
| x402 protocol | ✅ `@x402/express` + `@x402/stellar` |
| Addresses explicit demand signal | ✅ "pay-per-query web search instead of monthly subscriptions" |
4 changes: 2 additions & 2 deletions api/ai/chat.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { describe, it, expect, vi, beforeEach } from "vitest";

const mockCreate = vi.fn()
const mockCreate = vi.fn();

vi.mock('groq-sdk', () => ({
default: class {
Expand Down
26 changes: 21 additions & 5 deletions api/ai/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,25 @@ import type { VercelRequest, VercelResponse } from '@vercel/node'
import Groq from 'groq-sdk'
import { readServerConfig } from '../../src/lib/config'

export default async function handler(req: VercelRequest, res: VercelResponse) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' })
if (role === "system") {
return {
ok: false,
error: "Client messages may only use user or assistant roles",
};
}

if (role !== "user" && role !== "assistant") {
return {
ok: false,
error: "Client messages must use user or assistant roles",
};
}

if (typeof content !== "string") {
return { ok: false, error: "Message content must be a string" };
}

sanitized.push({ role, content });
}

const { messages, model: requestedModel } = (req.body || {}) as {
Expand Down Expand Up @@ -36,8 +52,8 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
const delta = chunk.choices?.[0]?.delta?.content
if (delta) sendEvent('delta', { content: delta })
}
sendEvent('done', { model })
res.end()
sendEvent("done", { model });
res.end();
} catch (err: any) {
if (controller.signal.aborted) return res.end()
console.error('[groq stream error]', err?.message)
Expand Down
Loading