A lightweight activity tracking service for Unicity Sphere. Records and streams user activities like token transfers, marketplace posts, and wallet creation events.
- Hono - Web framework
- Drizzle ORM - Database ORM
- SQLite (better-sqlite3) - Database
- Server-Sent Events (SSE) - Real-time streaming
npm install
cp .env.example .envnpm run devnpm run build
npm start# Create data directory with your user permissions
mkdir -p ./data
# Start container (uses your UID/GID to avoid permission issues)
UID=$(id -u) GID=$(id -g) docker compose up -d --build| Variable | Description | Default |
|---|---|---|
PORT |
Server port | 3001 |
DATABASE_URL |
SQLite database path | ./data/activities.db |
ACTIVITY_API_KEY |
API key for authentication | Auto-generated |
ALLOWED_ORIGINS |
Comma-separated origins that skip API key auth | (none) |
The service uses a dual authentication model:
-
Origin-based auth - Requests from origins listed in
ALLOWED_ORIGINSare allowed without an API key. This is for browser-based frontends that cannot securely store API keys. -
API key auth - All other requests (backend services, CLI tools) must include the API key in the
Authorizationheader.
Example ALLOWED_ORIGINS:
ALLOWED_ORIGINS=https://unicitynetwork.github.io,https://sphere.unicity.network,http://localhost:5173
Health check endpoint.
Response:
{ "status": "ok" }Fetch activities with pagination.
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
limit |
number | Max items to return (default: 50, max: 100) |
cursor |
string | Pagination cursor (activity ID) |
kind |
string | Filter by activity kind |
Response:
{
"activities": [
{
"id": 123,
"kind": "token_transfer",
"unicityId": "@alice",
"data": { "amount": "100", "symbol": "ALPHA" },
"isPublic": true,
"createdAt": "2025-01-20T12:00:00.000Z"
}
],
"nextCursor": "122"
}Server-Sent Events stream for real-time activity updates.
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
lastId |
string | Resume from this activity ID |
Event Format:
data: {"id":123,"kind":"token_transfer",...}
Create a new activity. Requires authentication.
Headers:
Content-Type: application/json
Authorization: Bearer <api_key> # Required if origin not in ALLOWED_ORIGINS
Request Body:
{
"kind": "token_transfer",
"unicityId": "@alice",
"data": { "amount": "100", "symbol": "ALPHA" },
"isPublic": true
}Valid kind values:
marketplace_post- User posted an item for saletoken_transfer- Token was transferredwallet_created- New wallet was createdgame_started- User started a gamebet_placed- User placed a betotc_purchase- OTC purchase completedmerch_order- Merchandise order placed
Response (201):
{
"id": 123,
"createdAt": "2025-01-20T12:00:00.000Z"
}SQLite database with auto-initialization. Tables are created on first run.
Schema:
CREATE TABLE activities (
id INTEGER PRIMARY KEY AUTOINCREMENT,
kind TEXT NOT NULL,
unicity_id TEXT,
data TEXT,
is_public INTEGER DEFAULT 0,
created_at INTEGER NOT NULL
)curl http://localhost:3001/activities?limit=10curl -X POST http://localhost:3001/activities \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{"kind": "token_transfer", "unicityId": "@alice", "data": {"amount": "100"}}'curl -N http://localhost:3001/activities/streamconst events = new EventSource('http://localhost:3001/activities/stream');
events.onmessage = (e) => {
const activity = JSON.parse(e.data);
console.log('New activity:', activity);
};