Files

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
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 ribbonComposerThread.

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: 1k10k)
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)
  1. Feature work — branch off staging, PR against staging
  2. Test — auto-deploys to staging.tinqs.com
  3. Release — merge stagingmain → deploys to platform.tinqs.com
  4. Hotfix — branch off main, merge to both main and staging

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 pullnpm ci → sync secrets to .env.localprisma generate / db pushnpm run buildpm2 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.