Independent NestJS service that will host Triskcraft's public API, OAuth/SSO, internal webhooks, temporary administration console and S3 file access.
This repository is the foundation described in Triskcraft/Triskcraft#5. The endpoint migrations remain separate follow-up work.
- Node.js 24.14 or newer
- pnpm 11.15.0 (declared through Corepack)
- PostgreSQL connection string
Copy-Item .env.example .env
corepack pnpm install
corepack pnpm start:devThe health check is available at GET /health and Swagger at /docs.
The API validates its environment during startup. DATABASE_URL is required
and must use the postgres:// or postgresql:// scheme. PORT defaults to
3000 and NODE_ENV defaults to development.
Only API-owned settings belong here. Identity signing keys, session secrets,
webhook HMAC secrets and Discord credentials must remain separate values. Do
not copy the bot's complete environment file or use a generic shared
JWT_SECRET.
The AuthModule exposes the compatible OAuth endpoints under /oauth and
supports authorization code grants with PKCE S256, Discord login, access and
ID tokens, refresh-token rotation and scope-filtered identity responses.
Identity tokens use the dedicated IDENTITY_PRIVATE_KEY and
IDENTITY_PUBLIC_KEY RSA pair. IDENTITY_KEY_ID is written as kid so the
pair can later be exposed through JWKS and rotated. Browser sessions use the
independent SESSION_SECRET; webhook and Discord credentials must never reuse
either identity or session key material.
The supported scopes are openid, identify and minecraft. OAuth clients,
redirect URIs and their allowed scopes continue to be managed in PostgreSQL.
Discord OAuth uses its own client credentials and DISCORD_REST_TOKEN only for
joining an authenticated user to the configured guild when necessary.
The internal endpoints /webhooks/digs, /webhooks/link and
/webhooks/join require a service JWT with the route-specific permission and
the following headers:
Authorization: Bearer <service-jwt>
X-Timestamp: <unix-seconds>
X-Signature: <sha256-hmac-hex>
The signature is calculated over the exact UTF-8 request bytes, without re-serializing JSON:
HMAC_SHA256(webhook-secret, timestamp + "." + rawBody)
Requests outside the existing 15-second timestamp window are rejected. The
per-token webhook secrets remain encrypted in PostgreSQL; the API decrypts
them with the independent WEBHOOK_ENCRYPTION_KEY. No secret values are
included in this documentation.
/webhooks/digs is processed through an in-memory NestJS provider every second.
Updates for the same player are coalesced to the latest value, reducing the
write frequency while keeping the batching window short. Drains are serialized
so overlapping scheduler invocations cannot apply stale values after newer
ones.
This queue is intentionally not persistent yet. The webhook returns 200 once
the payload is accepted into the process-local queue, not once the database
write completes. On Vercel, an invocation can be frozen or recycled after the
response, so events still in memory may be lost. A durable outbox/job table is
required if webhook delivery must survive process termination; the provider
can be replaced with that implementation without changing the controller.
The existing administrative interface is available under /console. It keeps
the OAuth/PKCE login, permission checks, modpack upload and role-management
operations from the former Express runtime. The HTML layout and components are
kept intentionally simple and compatible; this is a temporary home for the
console, which should later be extracted into an independent administration
application instead of being redesigned inside the API.
GET /files/:bucket/:filename streams objects from the configured S3-compatible
service and preserves the existing download headers. The public aliases remain
web (public-web) and blog (blog-media). The console uploads the SMP
modpack to public-web/pack-mods-triskcraftsmp.rar using multipart streaming.
The service consumes the generated Prisma Client from @triskcraft/db through
the global PrismaModule. It intentionally contains no Prisma schema or
migrations. Database migrations are run only by the database repository's
dedicated production workflow and never during API startup or deployment.
pnpm start:dev: local development serverpnpm build: compile the NestJS applicationpnpm lint: lint without modifying filespnpm format: format source filespnpm test: unit testspnpm test:e2e: HTTP integration testspnpm verify: final formatting, lint, test and build gate
Vercel's Git integration is not used. A push to main runs
.github/workflows/deploy.yml, verifies the project, builds with Vercel and
deploys the prebuilt output to production. The GitHub production environment
contains VERCEL_TOKEN, VERCEL_ORG_ID and VERCEL_PROJECT_ID; runtime values
such as DATABASE_URL are managed in the Vercel project.