Production · deployed from e4c77d6

buffcleb/teufli

Cross-platform gathering & event platform — hosts create events, invite guests by email, collect RSVPs, and (for paid events) coordinate P2P payments. Web, mobile, and an admin console over one typed API.

Tests492 passingTypeScriptstrictNode≥ 20Postgres16
Live Status
Checking…
https://api.teufli.com/health

Origin

Single-replicabehind Cloudflare Tunnel

Round-trip

ms

Health round-trip · livechecked
last msavg msmax ms
The name

Why “Teufli”?

An accidental cross-cultural pun — a French party word wearing a cozy Swiss-German sweater.

teufFrench · Verlan slang

fête flipped → teuf: a casual party, bash, or hangout. Widely used by younger French speakers.

+
-liSwiss-German · diminutive

the Müesli / Schoggi ending — it makes a word little, cute, and cozy.

Teufli

a cozy little gathering — endearing for reunions & close-knit crews

Architecture

The full-stack surface area

Six workspaces — api, web, mobile, admin, marketing, and a shared editor — over one typed API. Here's what powers each layer.

Frontend

  • React 18 + Vite 5

    Web app · app.teufli.com

  • Tailwind CSS 3

    Utility-first design system

  • TanStack Query 5

    Server-state & caching

  • React Router 6

    Client routing (web + admin)

  • Expo 56 · RN 0.85

    iOS + Android app

  • Astro 5

    Marketing site · teufli.com

  • @teufli/site-editor

    TipTap 3 event-site editor

Backend

  • Express 4 + TS 5.6

    Versioned /v1 REST API

  • Prisma 5

    Type-safe data access

  • Zod 3

    Runtime request validation

  • JWT HS256

    Passwordless magic links

  • In-process schedulers

    Reminders · email · SMS · audit

  • Admin API (:3001)

    Separate token & secret

Data & Storage

  • PostgreSQL 16

    Primary relational store

  • Prisma Migrate

    Versioned schema history

  • Signed media disk

    Sharded local · HMAC URLs

  • Redis (ioredis)

    Optional rate-limit store

  • AppConfig

    Server-side feature flags

  • Multer · Sharp

    Upload + image processing

Cloudflare & Ops

  • Cloudflare edge

    DNS · TLS · WAF · DDoS · cache

  • Cloudflare Tunnel

    cloudflared · outbound-only

  • Workers static assets

    web + marketing hosting

  • Podman + Portainer

    Containers on one host

  • Turnstile

    CAPTCHA on auth + RSVP

  • Cloudflare Access

    Gates admin.teufli.com

Request lifecycle — client to database

Clients

Web · iOS · Android · Admin

Cloudflare edge

TLS · WAF · cache · Turnstile

Cloudflare Tunnel

cloudflared · outbound-only

Express API

/v1 · Zod · Prisma · single-replica

PostgreSQL 16

Prisma · migrations

Signed media is edge-cached by Cloudflare; the tunnel keeps the origin off the public internet with no inbound ports. Mutations carry a double-submit CSRF token and terminate at the single-replica API, which owns the in-process reminder, email, SMS, and audit-retention schedulers.

API

Interactive API explorer

Browse the real 73-endpoint /v1 surface, grouped by area. Inspect payloads and copy a ready-to-run cURL for any endpoint.

It's Teufli's passwordless magic-link flow. The Bearer token is a 24-hour HS256 session JWT — the same one the mobile app stores. (Web clients use an httpOnly cookie instead, so this is the mobile / script / CI path.)

  1. 1

    Request a magic link for your account's email.

    bash
    curl -X POST "https://api.teufli.com/v1/auth/magic-link" \
      -H "Content-Type: application/json" \
      -d '{"email":"you@teufli.com"}'
  2. 2

    Open the emailed link, copy its ?token= value, and exchange it for the JWT.

    bash
    curl "https://api.teufli.com/v1/auth/verify?token=$MAGIC_TOKEN"
    # => { "token": "<JWT>", "user": { ... } }
  3. 3

    Export the returned token — now every Bearer example works.

    bash
    export TEUFLI_TOKEN="<JWT from the response>"

The magic token is one-time and expires in a few minutes — exchange it promptly; the JWT it returns lasts ~24h. In local dev the link isn't emailed, so read the token from the MagicToken table (or the API server logs) and run step 2.

Endpoints

73
POST/v1/eventsBearer JWT

Create an event — standalone or recurring. Enforces per-hour/day creation limits and the account event cap. The platform fee is copied onto the event server-side.

cURL
curl -X POST "https://api.teufli.com/v1/events" \
  -H "Authorization: Bearer $TEUFLI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title":"Bordeleau Family Reunion","description":"Annual get-together at the lake house.","location":"Lake House","startsAt":"2026-08-14T18:00:00.000Z","timezone":"America/New_York","visibility":"INVITE_ONLY","allowPlusOnes":true,"maxPlusOnes":4,"isPaid":true,"ticketPrice":20,"pricingMode":"PER_GUEST","paymentHandles":[{"type":"VENMO","handle":"@chris-b"}]}'
{ "title": "Bordeleau Family Reunion", "description": "Annual get-together at the lake house.", "location": "Lake House", "startsAt": "2026-08-14T18:00:00.000Z", "timezone": "America/New_York", "visibility": "INVITE_ONLY", "allowPlusOnes": true, "maxPlusOnes": 4, "isPaid": true, "ticketPrice": 20, "pricingMode": "PER_GUEST", "paymentHandles": [ { "type": "VENMO", "handle": "@chris-b" } ] }
{ "id": "evt_9f2a3c", "publicId": "a1B2c3D4", "slug": "bordeleau-family-reunion", "title": "Bordeleau Family Reunion", "status": "DRAFT", "startsAt": "2026-08-14T18:00:00.000Z", "visibility": "INVITE_ONLY", "isPaid": true, "ticketPrice": "20.00", "pricingMode": "PER_GUEST", "platformFeePct": "0.0300", "seriesId": null }
Infrastructure

Deployment topology

Not a multi-region edge fleet — one hardened origin. Cloudflare terminates and protects at the edge, a tunnel reaches in, and Podman runs the containers on a single host.

Web SPA

app.teufli.com

Mobile

iOS · Android

Marketing

teufli.com

Admin

admin.teufli.com

Cloudflare edgeglobal anycast
DNSTLS (Full strict)WAFDDoSBot FightCacheTurnstileAccess

Workers assets

web + marketing static

Cloudflare Tunnel

cloudflared · outbound-only

Single host · Podman + PortainerUbuntu · one region
teufli_api

Single-replica — owns in-process schedulers

teufli_admin_api:3001

Admin API · own JWT secret

teufli_admin:80

Admin console (nginx static)

teufli_postgres:5432

PostgreSQL 16 · internal only

teufli_redis:6379

Rate-limit store (optional)

cloudflaredtunnel

Outbound-only tunnel connector

Outbound servicesvia in-process schedulers
Resendemail queue

Transactional email · invites, magic links

TwilioSMS queue

SMS reminders (opt-in)

Expo Pushpush

Mobile notifications · exp.host

The main API is deliberately single-replica — it owns the in-process reminder, email, SMS, and audit-retention schedulers, so a second copy would double-fire them. Media is served signed and edge-cached; the admin console sits behind Cloudflare Access.

Containers & services
  • teufli_api:3000checking

    Main API · /v1 · signed /media

  • teufli_admin_api:3001

    Admin API · own JWT secret

  • teufli_admin:80

    Admin console (nginx static)

  • teufli_postgres:5432

    PostgreSQL 16 · internal only

  • teufli_redis:6379

    Rate-limit store (optional)

  • cloudflaredtunnel

    Outbound-only tunnel connector

The teufli_api dot reflects a real /health poll; the rest show their static role.

Security & Quality

Hardened at the edge, tested in depth

A defense-in-depth posture from Cloudflare's edge down to the API, backed by an all-green integration suite. Core Web Vitals is a tracked, not-yet-measured item.

Engineering quality
0%
Tests passing
492 / 492

492

Integration tests

vitest + supertest, real Postgres

6

Workspaces

api · web · mobile · admin · marketing · shared

50+

Prisma models

Single versioned schema

serial

Test runner

Single-fork · 2 retries · 20s timeout

Lighthouse & Core Web Vitalsplanned

Not yet measured — tracked as a feature request (Lighthouse CI). See docs/DASHBOARD-FEATURE-REQUESTS.md.

API surface · endpoints by area/v1
16
events
8
chat
7
auth
6
items
6
templates
5
guests
4
photos
4
polls
Security posture

Secrets

Host .env

Injected via Podman compose. JWT HS256 + separate admin secret + HMAC media signing — never in a client bundle.

DDoS

Cloudflare

Automatic L3/L4/L7 mitigation, always on at the edge in front of the tunnel.

WAF + Rate limiting

Managed

Cloudflare managed ruleset + express-rate-limit (auth capped at 20 / 15 min / IP).

TLS / SSL

Full (strict)

Cloudflare Full strict · min TLS 1.2 · HSTS enabled · auto-renewed certs.

CSRF defense

Double-submit

httpOnly teufli_session cookie + X-CSRF-Token header echo on every cookie-auth mutation.

Auth

Passwordless

Magic-link → 24h HS256 JWT. Per-guest inviteToken for RSVP; Turnstile on auth + RSVP.

Error contract{ error: { code, message, details } }
400VALIDATION_ERROR401UNAUTHORIZED403FORBIDDEN409RSVP_CLOSED409EVENT_AT_CAPACITY409LIMIT_EXCEEDED429RATE_LIMITED