API gateway and attestation service for Veritasor. Handles revenue data normalization, Merkle proof generation, and on-chain submission to Soroban contracts (integration points are stubbed for the initial version).
- Node.js + TypeScript
- Express for HTTP API
- Planned: PostgreSQL, Redis, gRPC internal services
- Node.js 18+
- npm or yarn
# Install dependencies
npm install
# Run in development (watch mode)
npm run devAPI runs at http://localhost:3000. Use PORT env var to override.
The shared rate limiter in src/middleware/rateLimiter.ts supports explicit route-level buckets. Apply a stable bucket name per sensitive route so bursts against one endpoint do not consume the budget for another endpoint. Auth routes use this for login, refresh, forgot-password, reset-password, and me, while signup keeps its dedicated abuse-prevention limiter.
Prometheus metrics are available at /metrics when METRICS_ENABLED=true.
Distributed tracing is disabled by default. Set OTEL_EXPORTER_OTLP_ENDPOINT to an OTLP/HTTP traces endpoint, such as http://localhost:4318/v1/traces, to initialize the OpenTelemetry Node SDK during app startup. The request logger creates one server span per HTTP request and Soroban RPC retries create child client spans, so slow attestation requests can be correlated with individual blockchain attempts.
Trace attributes intentionally exclude request bodies, headers, and raw query strings. Correlation IDs, HTTP method, route/path, status code, user agent, and Soroban operation metadata are emitted; exception messages are redacted before being recorded on custom spans.
| Command | Description |
|---|---|
npm run dev |
Start with tsx watch |
npm run build |
Compile TypeScript to dist/ |
npm run start |
Run compiled dist/index.js |
npm run lint |
Run ESLint |
npm run migrate |
Run database migrations |
npm run audit:ci |
Run dependency audit and allowlist validation |
This repository includes a GitHub Actions workflow at .github/workflows/security-audit.yml that runs:
pnpm audit --prod --jsonto detect vulnerabilities.scripts/check-audit.tsto enforce.audit-allowlist.jsonfor temporary, expiring exceptions.- A CycloneDX SBOM generation step that uploads
sbom/cyclonedx-sbom.xmlas a workflow artifact.
Allowlist entries must include:
id: Advisory identifierpackage: npm package nameseverity:low,moderate,high, orcriticalreason: Why the exception is allowedexpires: ISO 8601 expiration timestamp
Expired allowlist entries are rejected.
Routes may be mounted with an /api/v{n} prefix and/or legacy unversioned paths (e.g. /api/attestations). The server still resolves a major version for each request.
- Negotiation: Path segment wins when present; otherwise
X-API-Version,Accept-Version, queryapiVersion/api_version, thenAcceptparameters (version=,api-version=,v=). Default is v1. Unsupported majors fall back to v1 withAPI-Version-Fallback: true. - Response headers:
API-Version(always a supported label), optionalAPI-Version-Fallback, and mergedVaryfor caches. - Spec: docs/specs/api-version-negotiation.md
- Future extensions: Add entries to
SUPPORTED_API_VERSIONSand mount/api/v2routers when ready.
| Method | Path | Description | Auth Required |
|---|---|---|---|
| GET | /api/v1/health |
Health check | No |
| GET | /api/v1/attestations |
List attestations (stub) | User Auth |
| POST | /api/v1/attestations |
Submit attestation (stub) | User Auth |
| GET | /api/v1/businesses/me |
Get user business | User Auth |
| POST | /api/v1/businesses |
Create business | User Auth |
| PATCH | /api/v1/businesses/me |
Update business | User Auth |
The API uses JWT-based authentication. Include the token in the Authorization header:
Authorization: Bearer <your_jwt_token>For business-scoped operations, use the enhanced business authorization middleware:
Authorization: Bearer <your_jwt_token>
x-business-id: <business_id>Security Features:
- JWT token validation with user existence verification
- Business ownership enforcement (users can only access their own businesses)
- Input validation and injection prevention
- Detailed error responses with structured error codes
Error Codes:
MISSING_AUTH(401): Missing or invalid Authorization headerINVALID_TOKEN(401): Invalid, expired, or malformed JWT tokenMISSING_BUSINESS_ID(400): Business ID not provided or invalid formatBUSINESS_NOT_FOUND(403): Business not found or access denied
For detailed documentation, see Business Authorization Boundary Checks.
veritasor-backend/
├── src/
│ ├── db/
│ │ ├── migrations/ # SQL migrations (e.g. 001_create_users_table.sql)
│ │ └── migrate.ts # Migration runner
│ ├── routes/ # health, attestations
│ └── index.ts # Express app entry
├── package.json
└── tsconfig.json
Migrations live in src/db/migrations/ as numbered SQL files (e.g. 001_create_users_table.sql). The runner applies only pending migrations and records them in schema_migrations, so each runs once.
Local database setup (contributors)
The repo does not include database credentials. Install PostgreSQL locally, create a database (and optionally a user), then set DATABASE_URL in your .env using your own username, password, and database name. Example after installing Postgres: create a DB (e.g. createdb veritasor or via your GUI), then use a connection string like postgresql://localhost:5432/veritasor (or with a username/password if you created one).
How to run migrations
- Set
DATABASE_URL(PostgreSQL connection string), e.g. in.env(copy from.env.example). - Run:
npm run migrateOr with the CLI directly:
DATABASE_URL=postgresql://user:pass@localhost:5432/dbname npx tsx src/db/migrate.tsRequires Node 18+ and a running PostgreSQL instance.
Optional .env:
PORT=3000
DATABASE_URL=postgresql://user:password@localhost:5432/veritasor
This directory is its own git repository. To push to your remote:
git remote add origin <your-backend-repo-url>
git push -u origin main