Reputo is a privacy-preserving, modular reputation-and-voting platform inspired by Snapshot.
- Apps & Packages
- Quick Start
- Prerequisites
- Project Structure
- Algorithm Development
- Contributing
- License
- Team
| Path | Stack | Status | Links |
|---|---|---|---|
apps/api |
✅ Ready | 📚 README · 📖 API Docs | |
apps/ui |
✅ Ready | 📚 README · 🌐 App | |
apps/workflows |
✅ Ready | 📚 README | |
apps/typescript-worker |
✅ Ready | 📚 README | |
packages/reputation-algorithms |
✅ Ready | 📚 README | |
packages/algorithm-validator |
✅ Ready | 📚 README | |
packages/database |
✅ Ready | 📚 README | |
packages/storage |
✅ Ready | 📚 README |
# Install dependencies
pnpm install
# Build project
pnpm build
# Run all services in parallel
pnpm dev
# Run individual services
pnpm -F @reputo/api dev # API only# Basic local development setup
docker compose -f docker/docker-compose.dev.yml up --build- Node.js: 20.x or higher
- pnpm: 10.12.4 or higher
- Docker: For containerized development
- Git: With Lefthook for git hooks
- Docker & Docker Compose: Container orchestration
- Traefik: Reverse proxy
- Domain & DNS: For SSL certificate generation
- Cloudflare API Token: For DNS challenge
reputo/
├── apps/
│ ├── api/ # NestJS API server
│ ├── ui/ # Next.js frontend
│ ├── workflows/ # Temporal workflows
│ └── typescript-worker/ # Algorithm execution worker
├── packages/
│ ├── algorithm-validator/ # Shared Zod validation library
│ ├── reputation-algorithms/ # Algorithm definitions registry
│ ├── storage/ # Framework-agnostic S3 utilities
│ └── database/ # Mongoose database layer
├── scripts/
│ ├── create-algorithm.ts # Unified algorithm creation CLI
│ └── validate-algorithms.ts # Algorithm sync validation CLI
├── docker/
│ ├── docker-compose.yml # Production/staging setup
│ ├── docker-compose.dev.yml # Local development
│ ├── docker-compose.preview.yml # PR preview environments
│ ├── Dockerfile # Multi-stage build
│ └── traefik.yml # Reverse proxy config
├── .github/
│ ├── workflows/ # CI/CD pipelines
│ └── PULL_REQUEST_TEMPLATE.md
├── coverage/ # Test coverage reports
├── node_modules/ # pnpm workspace dependencies
├── package.json # Root workspace config
├── pnpm-workspace.yaml # Workspace definition
├── biome.json # Linting & formatting
├── lefthook.yml # Git hooks
├── vitest.config.ts # Test runner config
├── tsconfig.vitest.json # Vitest TS config
└── commitlint.config.mjs # Commit message linting
We follow a three-tier deployment strategy with automated promotion:
- Trigger: Adding
pullpreviewlabel to PRs - Infrastructure: AWS Lightsail
- URL: Dynamic subdomain generated per PR
- Cleanup: Auto-expires after 48h or PR closure
- Trigger: Merge to
mainbranch (automated) - URL:
- UI: staging.logid.xyz
- API: api-staging.logid.xyz
- Traefik: traefik-staging.logid.xyz/dashboard
- Deployment: Watchtower auto-pulls
stagingtagged images
- Trigger: Manual workflow dispatch with commit SHA
- URL:
- UI: logid.xyz
- API: api.logid.xyz
- Traefik: traefik.logid.xyz/dashboard
- Process: Promotes staging images with
productiontags
This section guides you through creating, configuring, and implementing reputation algorithms.
Algorithms in Reputo consist of two parts:
-
Algorithm Definition - A JSON schema that describes the algorithm's metadata, inputs, outputs, and runtime configuration. Located in
packages/reputation-algorithms/src/registry/. -
Activity Implementation - TypeScript code that executes the algorithm logic. Located in
apps/typescript-worker/src/activities/.
Use the unified CLI to create both the definition and activity scaffold in one command:
pnpm algorithm:create <key> <version>Example:
pnpm algorithm:create proposal_engagement 1.0.0This creates:
packages/reputation-algorithms/src/registry/proposal_engagement/1.0.0.json- Algorithm definitionapps/typescript-worker/src/activities/proposal_engagement.activity.ts- Activity scaffold
Requirements:
keymust besnake_case(e.g.,voting_engagement,proposal_score)versionmust be valid SemVer (e.g.,1.0.0,2.1.0-beta)
Edit the generated JSON file to define your algorithm's schema:
{
"key": "proposal_engagement",
"name": "Proposal Engagement",
"category": "Engagement",
"description": "Calculates user engagement based on proposal interactions",
"version": "1.0.0",
"inputs": [
{
"key": "proposals",
"label": "Proposals CSV",
"type": "csv",
"csv": {
"hasHeader": true,
"columns": [
{ "key": "user_id", "type": "string" },
{ "key": "proposal_id", "type": "string" },
{
"key": "action",
"type": "enum",
"enum": ["view", "vote", "comment"]
}
]
}
}
],
"outputs": [
{
"key": "engagement_scores",
"label": "Engagement Scores",
"type": "csv",
"csv": {
"columns": [
{ "key": "user_id", "type": "string" },
{ "key": "score", "type": "number" }
]
}
}
],
"runtime": {
"taskQueue": "typescript-worker",
"activity": "proposal_engagement"
}
}Key fields:
| Field | Description |
|---|---|
inputs |
Define expected input data schema (CSV columns, types, constraints) |
outputs |
Define output data schema |
runtime.activity |
Must match the exported function name in the activity file |
Edit the generated activity file to implement your algorithm:
// apps/typescript-worker/src/activities/proposal_engagement.activity.ts
export async function proposal_engagement(
payload: WorkerAlgorithmPayload
): Promise<WorkerAlgorithmResult> {
const { snapshotId, algorithmKey, inputLocations } = payload
// 1. Get input data
const inputKey = getInputLocation(inputLocations, 'proposals')
const buffer = await storage.getObject(inputKey)
const rows = parse(buffer.toString('utf8'), { columns: true })
// 2. Implement your algorithm logic
const scores = computeEngagementScores(rows)
// 3. Serialize and upload output
const outputCsv = stringify(scores, { header: true })
const outputKey = generateSnapshotOutputKey(snapshotId, algorithmKey)
await storage.putObject(outputKey, outputCsv, 'text/csv')
// 4. Return output locations
return {
outputs: {
engagement_scores: outputKey,
},
}
}Ensure all algorithm definitions have corresponding activity implementations:
pnpm algorithm:validateThis checks:
- Every definition has a matching activity file
- Every definition has a matching activity export
- Every activity file is exported in the index
# Validate and build the algorithms package
pnpm --filter @reputo/reputation-algorithms build
# Build the worker
pnpm --filter @reputo/typescript-worker build
# Run tests
pnpm testTo add a new version of an existing algorithm:
pnpm algorithm:create voting_engagement 2.0.0This creates a new version file. The activity implementation is shared across versions unless you need version-specific logic.
| Command | Description |
|---|---|
pnpm algorithm:create <key> <version> |
Create algorithm definition + activity scaffold |
pnpm algorithm:validate |
Validate definitions and activities are in sync |
pnpm --filter @reputo/reputation-algorithms algorithm:create <key> <version> |
Create definition only |
pnpm --filter @reputo/typescript-worker algorithm:new <key> |
Create activity only |
-
Create feature branch from
maingit checkout -b feature/your-feature-name
-
Open Pull Request to
main- Add
pullpreviewlabel for preview deployment - Ensure CI passes
- Request review from maintainers
- Add
-
Merge after approval
Released under the GPL-3.0 license. See LICENSE file for details.
![]() |
![]() |
![]() |
|---|---|---|
| Cyrille Derche | Mohammad Khaki | Behzad Rabiei |



