Clarification: All the code was modified from an official Midnight example. Some untouched files retain the last modification date from the example code. All of the Aegis code was first created during the Midnight x MLH hackathon.
Market intelligence powered by anonymous ZK signals. Stores see what customers want. Customers stay invisible.
Built for the Midnight Network hackathon.
Aegis is a privacy-preserving market intelligence system built on Midnight Network. It solves a fundamental tension in commerce: stores need to know what customers want, but customers don't want to be tracked.
How it works:
- Users contribute anonymous purchase signals (by category) to a Midnight smart contract. Each signal generates a ZK proof — the blockchain only knows "one more person bought in electronics", never who.
- The aggregate state (total signals per category) is public and verifiable on-chain.
- An LLM agent (Claude) reads the aggregate and generates market intelligence — trending categories, insights, and recommendations — without ever accessing individual user data.
- Stores create targeted campaigns with a minimum signal threshold. When enough signals accumulate in a category, their campaign activates.
┌─────────────────┐ ZK proof ┌─────────────────────┐
│ User Device │ ──────────────── ▶ │ Midnight Contract │
│ (anonymous) │ │ (aggregate ledger) │
└─────────────────┘ └──────────┬──────────┘
│ readState
┌──────────▼──────────┐
│ TypeScript API │
│ + Claude Agent │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ React Frontend │
│ Store + User view │
└─────────────────────┘
Stack:
- Smart contract: Compact (Midnight's ZK language) —
contracts/aegis.compact - Backend: Node.js + TypeScript, raw HTTP server
- LLM Agent: Anthropic Claude API (market insights + campaign matching reasoning)
- Frontend: React + Vite
aegis/
├── contracts/
│ ├── aegis.compact # Compact smart contract
│ ├── managed/aegis/ # Compiled artifacts (proving keys, zkir)
│ └── index.ts # TypeScript exports
├── src/
│ ├── index.ts # Entry point — deploy or reuse contract
│ ├── api.ts # HTTP API server (port 3001)
│ ├── agent.ts # Claude LLM agent
│ ├── contract.ts # Contract interaction helpers
│ ├── providers.ts # Midnight providers setup
│ ├── wallet.ts # Wallet integration
│ └── config.ts # Network config
├── frontend/
│ ├── src/
│ │ ├── App.tsx # Two-tab app (Store / User)
│ │ ├── api.ts # API client
│ │ └── components/
│ │ ├── StoreView.tsx # Market intelligence + campaign management
│ │ └── UserView.tsx # Signal contribution UI
│ └── vite.config.ts
└── docker-compose.yml # Midnight local infrastructure
contracts/aegis.compact tracks aggregate purchase signals per category using on-chain Counters. No individual user data is stored.
export enum Category { electronics, fashion, food, sports, home, other }
export ledger signalsElectronics: Counter;
export ledger signalsFashion: Counter;
// ... one Counter per category
export ledger totalSignals: Counter;
export ledger campaignCount: Counter;
export circuit contributeSignal(category: Category): [] {
// Each call generates a ZK proof — the caller is never revealed
if (disclose(category == Category.electronics)) { signalsElectronics.increment(1); }
// ...
totalSignals.increment(1);
}
- Node.js >= 22
- Yarn
- Docker + Docker Compose
- Anthropic API key
yarn env:upThis starts the Midnight proof server, node, and indexer via Docker Compose.
cp .env.example .env
# Edit .env and add your ANTHROPIC_API_KEYyarn install
cd frontend && yarn install && cd ..yarn startOn first run, it deploys the Aegis contract to the local Midnight node and saves the address to .contract-address. Subsequent runs reuse the same contract.
cd frontend
yarn dev| Method | Path | Description |
|---|---|---|
POST |
/signal |
Contribute an anonymous signal { category: "electronics" } |
GET |
/state |
Read aggregate signal counts from the contract |
GET |
/insights |
Claude generates market intelligence from the aggregate |
POST |
/campaigns |
Register a campaign { targetCategory, minSignals, message } |
GET |
/match/:id |
Check if a campaign threshold is met (with Claude reasoning) |
for cat in electronics fashion food sports home other; do
for i in $(seq 1 5); do
curl -s -X POST http://localhost:3001/signal \
-H "Content-Type: application/json" \
-d "{\"category\": \"$cat\"}" > /dev/null
echo "Signal: $cat ($i)"
done
done- Zero individual tracking: The contract only stores counters, never user identities or addresses.
- ZK proofs: Every
contributeSignalcall generates a proof that validates the category without revealing the caller. - Verifiable aggregate: The signal counts on-chain are publicly auditable — stores can trust the numbers.
- Agent operates on aggregate only: Claude never sees individual transactions, only the totals.
| Variable | Description |
|---|---|
ANTHROPIC_API_KEY |
Your Anthropic API key for the LLM agent |
