Tinqs Platform (Tinqs Forge)
2D/3D game asset generation SaaS — credit-based freemium. Generate style-locked sprites, meshes, and full item sets from text prompts and reference images. Built by Tinqs for Ariki and beyond.
Product URL: Tinqs Forge at platform.tinqs.com (staging: staging.tinqs.com).
This repo (origin)
| What | Name |
|---|---|
| Git Studio repo (canonical) | tinqs-ltd/platform |
git remote origin |
https://git.arikigame.com/tinqs-ltd/platform.git |
| Local folder (common on dev machines) | tinqs-tools — same repo, different directory name |
| Deploy / pm2 name (prod) | platform |
| PostgreSQL database | platform |
Clone:
git clone https://git.arikigame.com/tinqs-ltd/platform.git
# often checked out as ~/tinqs-ltd/tinqs-tools
Related repos (Ariki Studio + hub)
| Repo | Role |
|---|---|
tinqs-ltd/bot |
Tinqs Team Tool binary + Ariki Studio UI (cli/transcribe/) |
tinqs-ltd/docs |
Hub — agents, handoff, team docs |
tinqs-ltd/devops |
Infra, DNS, deploy notes |
Ariki Studio (Team Tool UI)
Design docs live in this repo (platform / tinqs-tools). Source code for Studio + Shell lives in bot.
The desktop Tinqs Team Tool embeds Ariki Studio — voice → Transcript ribbon → Composer → Thread.
Doc (here in platform) |
Contents |
|---|---|
studio-ui-map.md |
Section names, layout, component registry |
design-system.md |
Colors, type, motion (ads-* tokens) |
Doc (in bot repo) |
Contents |
|---|---|
cli/transcribe/STUDIO.md |
data-component + function names in code |
cli/transcribe/ide.html |
Studio components (Thread, Composer, …) |
cli/transcribe/main.go |
Shell components (Command bar, Transcript ribbon, …) |
Example jargon: “Move EL lines from the transcript ribbon into the composer, postComposer adds a user post to the thread; runAgentOnThread calls the model.”
Stack
| Layer | Tech |
|---|---|
| Framework | Next.js 14 (App Router) |
| Language | TypeScript |
| Styling | Tailwind CSS |
| Database | Prisma + PostgreSQL (shared localhost instance with Game Caster) |
| Queue | pg-boss (PostgreSQL-backed — no Redis needed) |
| Cache | SHA-256 content-addressable (prompt+style+seed) → Asset table |
| Storage | S3 — tinqs-platform-assets eu-west-1 |
| Auth | NextAuth.js — Google OAuth + dev login |
| Payments | Stripe Checkout + Webhooks |
| 2D Gen | FLUX.2 Klein 4B via fal.ai |
| 3D Gen | TRELLIS.2 via fal.ai (decimation targets: low=25k, med=100k, high=500k verts) |
| Style Extraction | Claude Sonnet 4 (Anthropic) |
| GDD Parsing | DeepSeek V4 |
How To Develop
Prerequisites
- Node.js 18+
- PostgreSQL 16 — installed at
C:\Program Files\PostgreSQL\16(shared with Game Caster) - No Redis needed — pg-boss uses the same PostgreSQL database for queuing
1. Create the database (piggyback on Game Caster's Postgres)
$env:PGPASSWORD="gamecaster"
& "C:\Program Files\PostgreSQL\16\bin\psql.exe" -h localhost -p 5432 -U gamecaster -d postgres -c "CREATE DATABASE platform OWNER gamecaster;"
2. Set up the app
cd platform
npm install
cp .env.example .env.local # edit keys as needed
npx prisma db push
npx prisma generate
npm run dev -- -p 3109
3. Dev Login (skip Google OAuth locally)
Set AUTH_DEV_LOGIN_ENABLED=true in .env.local. Enter any email on /login to skip Google OAuth.
4. Verify it all works
# Postgres
& "C:\Program Files\PostgreSQL\16\bin\psql.exe" -h localhost -U gamecaster -d platform -c "\dt"
# App
curl http://localhost:3109 # should return HTML
Environment Variables
| Variable | Purpose |
|---|---|
DATABASE_URL |
postgresql://gamecaster:gamecaster@localhost:5432/platform |
GOOGLE_CLIENT_ID |
Google OAuth |
GOOGLE_CLIENT_SECRET |
Google OAuth |
NEXTAUTH_URL |
Auth callback URL (must match deployment) |
NEXTAUTH_SECRET |
JWT signing secret (openssl rand -hex 32) |
FAL_KEY |
fal.ai API key |
FAL_WEBHOOK_URL |
fal webhook endpoint (not needed for local dev) |
ANTHROPIC_API_KEY |
Claude API key (style extraction) |
DEEPSEEK_API_KEY |
DeepSeek API key (GDD parsing) |
STRIPE_SECRET_KEY |
Stripe secret key |
STRIPE_WEBHOOK_SECRET |
Stripe webhook signing secret |
STRIPE_PRICE_* |
Stripe price IDs for credit packages |
AUTH_DEV_LOGIN_ENABLED |
Set true for local dev login bypass |
NEXT_PUBLIC_URL |
Public-facing URL |
AWS_ACCESS_KEY_ID |
S3 access key (asset persistence) |
AWS_SECRET_ACCESS_KEY |
S3 secret key |
AWS_S3_REGION |
eu-west-1 |
AWS_S3_BUCKET |
tinqs-platform-assets |
API Routes
| Route | Description |
|---|---|
GET /api/ping |
Health check (deploy + load balancers) |
POST /api/generate/text2img |
Text → 2D image |
POST /api/generate/img2mesh |
2D image → 3D mesh |
POST /api/generate/full |
Text → 2D → 3D (full pipeline) |
POST /api/generate/style-extract |
Reference images → StyleDNA (Claude) |
POST /api/generate/read-gdd |
GDD text → item set suggestions (DeepSeek) |
POST /api/generate/set |
Batch item set generation |
POST /api/fal/webhook |
fal.ai completion webhook (S3 persist on result) |
POST /api/stripe/create-checkout |
Create Stripe checkout session |
POST /api/stripe/webhook |
Stripe webhook handler |
POST /api/keys |
Create API key |
Credit Costs
| Action | Credits |
|---|---|
| Text → Image | 1 |
| Image → Mesh | 5 |
| Full pipeline | 6 |
| Style extraction | 2 |
| Styled text → image | 1 |
| Styled full pipeline | 6 |
TRELLIS.2 Quality Presets
Matching Melih's BK vertex budget targets:
| Quality | Vertices | Resolution | Texture | Use case |
|---|---|---|---|---|
low |
25,000 | 512 | 1024 | Ariki game-ready props (BK range: 1k–10k) |
medium |
100,000 | 1024 | 2048 | Standard quality previews |
high |
500,000 | 1536 | 4096 | Maximum detail, offline use |
Architecture
API Route fal.ai Webhook
──────── ────── ───────
1. Check cache (Asset table)
2. If hit → return S3 URL
3. If miss → fal.queue.submit ──→ Queue ──→ Complete ──→ POST /api/fal/webhook
with webhookUrl │
4. Return { pending: true } ├─ Download from fal URL
├─ Upload to S3
└─ Save Asset record
All generated assets are permanently stored on S3. Same prompt + style + seed = same cached S3 URL (no re-generation, no credit cost).
Project Memory
Like Cursor's MEMORY.md — each project accumulates editable requirements and feedback:
User creates "Ariki Weapons" project
→ sets style + quality targets
→ generation injects project context into prompt
User gives feedback: "sword blade too thin"
→ stored in project.feedbackHistory
→ next sword generation sees: "Important: sword blade too thin"
Gitflow — Trunk-based with release branches
main ← production (platform.tinqs.com)
staging ← staging (staging.tinqs.com)
- Feature work — branch off
staging, PR againststaging - Test — auto-deploys to
staging.tinqs.com - Release — merge
staging→main→ deploys toplatform.tinqs.com - Hotfix — branch off
main, merge to bothmainandstaging
Production URLs
| Environment | Branch | URL |
|---|---|---|
| Staging | staging |
https://staging.tinqs.com |
| Production | main |
https://platform.tinqs.com |
Deployment (Lightsail — same pattern as bot)
No Vercel. Gitea Actions runs on the host runner (runs-on: host) on the Lightsail box: git pull → npm ci → sync secrets to .env.local → prisma generate / db push → npm run build → pm2 restart.
| Environment | Branch | Checkout dir | pm2 name | Local port | Public host |
|---|---|---|---|---|---|
| Staging | staging |
$HOME/platform-staging |
platform-staging |
3115 | staging.tinqs.com |
| Production | main |
$HOME/platform-prod |
platform |
3120 | platform.tinqs.com |
tinqs-proxy routes those hostnames to localhost (see tinqs-ltd/bot/proxy/main.go). DNS: point staging.tinqs.com and platform.tinqs.com A/AAAA records at the same public IP as git.arikigame.com (Lightsail), then rebuild + restart proxy if you add new routes.
One-time setup on the Lightsail box
SSH in as the user that runs Gitea Actions (same as bot deploy). Use a deploy key or PAT with repo read access:
# Staging checkout
git clone -b staging https://git.arikigame.com/tinqs-ltd/platform.git ~/platform-staging
cd ~/platform-staging && npm ci && cp .env.example .env.local # then fill .env.local manually OR rely on Gitea secrets only
# Production checkout
git clone -b main https://git.arikigame.com/tinqs-ltd/platform.git ~/platform-prod
cd ~/platform-prod && npm ci && cp .env.example .env.local
After the first successful workflow run, secrets from Git Studio overwrite the variables listed in the workflow (empty secrets skip that key).
Gitea Actions secrets (Git Studio → repo → Settings → Actions → Secrets)
Staging — prefix STAGING_ (values are full URLs/keys for staging):
STAGING_DATABASE_URL, STAGING_NEXTAUTH_URL, STAGING_NEXTAUTH_SECRET, STAGING_GOOGLE_CLIENT_ID, STAGING_GOOGLE_CLIENT_SECRET, STAGING_NEXT_PUBLIC_URL, STAGING_FAL_KEY, STAGING_FAL_WEBHOOK_URL (e.g. https://staging.tinqs.com/api/fal/webhook), STAGING_ANTHROPIC_API_KEY, STAGING_DEEPSEEK_API_KEY, Stripe keys, S3 keys (STAGING_AWS_*).
Production — prefix PROD_:
PROD_DATABASE_URL, PROD_NEXTAUTH_URL, … same shape; PROD_FAL_WEBHOOK_URL = https://platform.tinqs.com/api/fal/webhook.
Proxy reload after route changes
On the box, rebuild and restart tinqs-proxy (see bot/proxy/deploy.sh or the build-proxy Gitea workflow).
Health check
Deploy workflows curl http://127.0.0.1:<port>/api/ping after pm2 restart.