Event layer for production AI agents — aventer.dev
Aventer normalizes agent events into a canonical schema, delivers them reliably to your endpoints, and gives you a live view of what your agents are doing without opening an IDE.
aventer/
├── packages/
│ ├── schema/ @aventer/schema — agent-v1 types + JSON Schema
│ ├── delivery/ @aventer/delivery — signing, retries, URL validation
│ ├── sdk/ @aventer/sdk — emit(), configure()
│ └── cli/ @aventer/cli — aventer init | login | listen
├── services/
│ ├── api/ Ingest API + SSE live stream
│ ├── worker/ Delivery + retry + DLQ (Phase 2)
│ └── dashboard/ Live event viewer (beta)
└── examples/
npm install
npm run build
# Terminal 1 — API
npm run dev:api
# Terminal 2 — dashboard
npm run dev:dashboard
# Terminal 3 — configure + emit
cd examples
npx tsx emit.tsBeta authentication is now enabled!
Default login credentials:
- Username:
admin - Password:
changeme123
There is no password-change UI yet — use the API:
# 1. Get an admin token
TOKEN=$(curl -s -X POST https://api.aventer.dev/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"changeme123"}' \
| jq -r '.token')
# 2. Set a new password (default admin id: usr_admin_default)
curl -s -X PUT https://api.aventer.dev/v1/admin/users/usr_admin_default/password \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"password":"YOUR_NEW_STRONG_PASSWORD"}'Expect {"updated":true}. Log out of the dashboard and sign in with your new password.
To look up a user id: curl -s -H "Authorization: Bearer $TOKEN" https://api.aventer.dev/v1/admin/users | jq
Alternative: create a new admin at /admin.html, log in as that user, then delete the default admin account. See BETA_TESTER_GUIDE.md.
For SDK/API access, use the default beta API key (local only): avn_beta_dev_key_change_me
Set AVENTER_API_KEY or use:
npx aventer login --api-key avn_beta_dev_key_change_me
npx aventer init --api-url http://localhost:3001import { configure, emit } from "@aventer/sdk";
configure({
apiKey: process.env.AVENTER_API_KEY,
apiUrl: process.env.AVENTER_API_URL ?? "http://localhost:3001",
agentId: "my-agent",
});
await emit("task.started", { task_id: "123" });
// ... agent work ...
await emit("task.completed", { task_id: "123", tokens: 4200 });agent-v2 (recommended): structured cost/correctness fields, guardrail events, verify() for eval pipeline. See docs/AGENT_V2_SPEC.md.
import { configure, emit, verify } from "@aventer/sdk";
await emit("task.completed", {
task_id: "123",
tokens: { input: 3000, output: 1200, total: 4200 },
cost_usd: 0.042,
correctness: "unknown",
});
await verify({
task_id: "123",
evaluator: "golden-set-v1",
evaluator_type: "golden_set",
score: 0.95,
verdict: "pass",
});npm run emit:v2 # example v2 flow with verify()Fetch run summary: GET /v1/runs/:run_id
| Environment | URL |
|---|---|
| Marketing | aventer.dev |
| Beta API | api.aventer.dev |
| Beta dashboard | beta.aventer.dev |
Local dev uses localhost:3001 (API) and localhost:5173 (dashboard).
Only deploy the dashboard on Vercel — the API runs on Hetzner (see DEPLOYMENT_PLAN.md).
| Setting | Value |
|---|---|
| Root Directory | services/dashboard |
| Framework Preset | Other (or leave as detected; vercel.json overrides build) |
| Build Command | (from services/dashboard/vercel.json) cd ../.. && npm run build:dashboard |
| Output Directory | dist |
| Install Command | (from services/dashboard/vercel.json) cd ../.. && npm ci |
Use services/dashboard as Root Directory. Vercel defaults to the repo root or may detect services/api; either causes a failed build (Cannot find module '@aventer/schema') because the API is not a Vercel target.
Do not set Root Directory to:
.(repo root) — unless you prefer the rootvercel.json;services/dashboardis simplerservices/api— backend service; deploy on Hetzner instead
| Name | Value |
|---|---|
VITE_API_URL |
https://api.aventer.dev |
Also set in services/dashboard/vercel.json for builds. Production builds default to https://api.aventer.dev if unset. Without this, the dashboard calls /v1/events/stream on Vercel (404) instead of the API.
Required in production so the dashboard SSE client talks to your API host (local dev uses the Vite proxy when this is unset).
beta.aventer.dev→ this Vercel project
- Import github.com/deboboy/aventer in Vercel
- Project Settings → General → Root Directory → set to
services/dashboard→ Save - Project Settings → Environment Variables → add
VITE_API_URL - Redeploy
-
agent-v1schema - Ingest API (
POST /v1/events) - SDK
emit() - CLI
init,login,listen,status - Dashboard SSE live stream
- Postgres event persistence
- Delivery worker + HMAC signing + DLQ
- Subscriber registry API
- Beta authentication + admin dashboard
- SDK
createHandler()for subscribers -
aventer listentunnel to cloud
See ~/LastMyle/Content/Agents Events Webhooks/AEW_BETA_PROTOTYPE_PLAN.md for the full beta plan.
Local clone: ~/LastMyle/Code/aventer
docker compose up -d
cp services/api/.env.example services/api/.env
# or add DATABASE_URL to repo .env.local
npm run build:api
DATABASE_URL=postgresql://aventer:aventer@localhost:5432/aventer npm run dev:api
curl -s http://localhost:3001/health
# {"status":"ok","service":"aventer-api","db":"connected"}On the VPS as root (from a fresh clone or after git pull):
cd /opt/aventer
chmod +x scripts/postgres-vps-setup.sh
./scripts/postgres-vps-setup.shThe script prints a DATABASE_URL — add it to /etc/aventer/env:
sudo nano /etc/aventer/envDATABASE_URL=postgresql://aventer:<password>@localhost:5432/aventerDeploy API code and restart:
sudo -u aventer git -C /opt/aventer pull origin main
sudo -u aventer bash -lc 'cd /opt/aventer && npm ci && npm run build:api'
sudo systemctl restart aventer-api
curl -s https://api.aventer.dev/health
# expect "db":"connected"Migrations run automatically on API startup. Manual run: npm run db:migrate -w @aventer/api (after build).
Outbound webhooks: when events are ingested, matching subscribers get queued deliveries. The worker POSTs signed payloads with retries (6 attempts) then DLQ.
curl -s -X POST https://api.aventer.dev/v1/subscribers \
-H "Authorization: Bearer $AVENTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/aventer",
"secret": "whsec_your_secret",
"event_types": ["task.completed", "task.failed"]
}'Omit event_types (or []) to receive all events. URLs must be HTTPS (no private IPs).
# Terminal 1
WEBHOOK_SECRET=whsec_test node examples/webhook-receiver.mjs 4000
# Terminal 2 — register (use ngrok/cloudflared URL in production test)
curl -X POST http://localhost:3001/v1/subscribers \
-H "Authorization: Bearer avn_beta_dev_key_change_me" \
-H "Content-Type: application/json" \
-d '{"url":"https://YOUR_TUNNEL/webhook","secret":"whsec_test"}'
# Terminal 3
npm run emitsudo cp /opt/aventer/scripts/aventer-worker.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now aventer-worker
sudo systemctl status aventer-workerDeploy: npm run build:worker then systemctl restart aventer-worker.
curl -s -H "Authorization: Bearer $AVENTER_API_KEY" \
"https://api.aventer.dev/v1/deliveries?status=dlq"
curl -s -X POST -H "Authorization: Bearer $AVENTER_API_KEY" \
"https://api.aventer.dev/v1/deliveries/del_xxx/replay"API (/etc/aventer/env on VPS, or services/api/.env locally):
PORT=3001
NODE_ENV=production
AVENTER_BETA_API_KEY=avn_beta_<secret>
JWT_SECRET=<openssl rand -base64 32>
DATABASE_URL=postgresql://aventer:<password>@localhost:5432/aventerWithout DATABASE_URL, the API falls back to in-memory storage (events lost on restart).
Copy services/api/.env.example for local overrides.