Documentation

Live Content System

Versioned content delivery platform for PsycatGames mobile apps – Truth or Dare, Would You Rather, Never Have I Ever, and others. The system manages the full lifecycle from authoring statements to delivering them as encrypted, versioned payloads to mobile clients.

How content flows

Author statements → Organize into categories → Create snapshot → Map to app version → App fetches via API
  1. Statements are created (manually or via CSV import) following a schema that defines their text structure
  2. Categories use filter rules to dynamically group statements – no manual assignment
  3. Snapshots freeze the current state of all categories and their resolved statements into an immutable payload
  4. Version mappings tie a published snapshot to a specific app version (semver)
  5. Mobile apps call the client API with their bundle ID and version. The API resolves the correct snapshot, filters by platform and locale, encrypts the response, and returns it

Architecture

┌─────────────────────────────────────────────────────────────┐
│  Admin UI (content-tool.vanilla.nl)                         │
│  Hugo + Tailwind + Vanilla JS                               │
└──────────────────────┬──────────────────────────────────────┘
                       │ REST API
┌──────────────────────▼──────────────────────────────────────┐
│  Admin Backend (content-backend.vanilla.nl)                  │
│  Express 5 + TypeScript                                      │
│  Content CRUD, snapshots, imports, translations, auth        │
└──────────────────────┬──────────────────────────────────────┘
                       │
        ┌──────────────▼──────────────┐
        │  PostgreSQL 16 + pgvector   │
        │  Redis 7 (cache + nonces)   │
        └──────────────┬──────────────┘
                       │
┌──────────────────────▼──────────────────────────────────────┐
│  Client API (content-api.vanilla.nl)                         │
│  Express 5 + TypeScript                                      │
│  HMAC auth, AES-256-GCM encryption, version resolution       │
└──────────────────────┬──────────────────────────────────────┘
                       │ Encrypted responses
              ┌────────▼────────┐
              │  Mobile Apps    │
              │  iOS / Android  │
              └─────────────────┘

Services

ServiceURLPurpose
Admin UIcontent-tool.vanilla.nlBrowser-based content management
Admin Backendcontent-backend.vanilla.nlREST API for the admin UI (Google OAuth)
Client APIcontent-api.vanilla.nlContent delivery to mobile apps (HMAC + encryption)

Roles

Every user is assigned one or more roles that control what they can see and do:

RoleContentSnapshotsSchemasImportsUsersLogs
ADMINFullFullFullFullFullFull
DEVFullFullFullFull
CONTENTFullFull
DESIGNRead + Edit

What’s in this documentation

Content Managers

Creating and editing statements, organizing content with categories, running AI translations, and collaborating through Google Sheets.

Developers

Client API reference (HMAC auth, encryption, endpoints), schema types, snapshot publishing, version mapping, and release workflow.

Admins

User and role management, system logs, and monitoring.

If you’re new, start with the Getting Started guide.

Getting Started

First-time setup

This walks through setting up a new app from scratch and publishing content to mobile clients. You’ll need at least the DEV role to create schemas and apps.

1. Create a Schema Type

Go to Schema Types in the sidebar. Click Create Schema.

A schema defines the text structure of your statements. It has:

  • Parts – an ordered list of named text segments. Regular parts (like prefix, option1) are user-editable text fields. Parts in {} (like {OR}) are literals with fixed translations.
  • Join string – character(s) used to combine parts into full text (usually a space)
  • Literals – static text with translations per locale, used for connector words

Example: A “Would You Rather” schema with parts ["title", "text1", "text2"] and join " " produces statements like “Would you rather eat a bug drink sour milk” (the app handles displaying the “or” between options).

Content Managers

Content management

This section covers the day-to-day content work: creating and editing statements, organizing them into categories, translating content, and collaborating with your team through Google Sheets.

The key tabs you’ll use:

  • All Statements – Browse, search, filter, and edit the full statement library
  • Categories – Create and manage categories with filter-based rules
  • Translations – Run AI-powered translation jobs across 20+ languages
  • Sheets Sync – Export statements to Google Sheets for team editing, then import changes back

Developers

Developer documentation

This section covers everything a developer needs to integrate a mobile app with the content system, understand the data model, and manage releases.

System architecture

The content system has two separate APIs:

Admin Backend (content-backend.vanilla.nl) – REST API for the admin UI. Handles content CRUD, user auth (Google OAuth), snapshots, imports, and translations. Not used by mobile apps.

Client API (content-api.vanilla.nl) – Content delivery API for mobile apps. Uses HMAC-SHA256 authentication and AES-256-GCM response encryption. Serves published snapshot data based on app version, locale, and platform.

Admins

Administration

This section covers system administration: managing users and roles, monitoring system health through logs, and managing API keys.

Only users with the ADMIN role can access the Administration and System Logs tabs.