Getting started with Gym

Clone the source, configure PostgreSQL, sessions, and S3 storage, apply migrations, create the first operator, run onboarding, and deploy.

Requirements

  • Node.js. package.json declares >=20.0.0; Next.js 16 needs 20.9 or later.
  • npm (the repository ships a package-lock.json).
  • A PostgreSQL database you are allowed to migrate. DATABASE_URL must start with postgres:// or postgresql://.
  • S3-compatible object storage for images (member profile photos) in production.

Hosted app

The hosted app is at gym.openship.org. The staff dashboard is at /dashboard on the same host.

Install

git clone https://github.com/openshiporg/openfront-gym.git
cd openfront-gym
npm install
cp env.example .env

The example file is named env.example (no leading dot).

Environment

features/keystone/index.ts runs in strict mode when NODE_ENV=production or GYM_STRICT_CONFIG=true. In strict mode it refuses to start without DATABASE_URL, a SESSION_SECRET of at least 32 characters, and all five S3 values. Outside strict mode it falls back to a local database URL, a test session secret, and placeholder S3 values.

DATABASE_URL=postgresql://user:password@127.0.0.1:5432/openfront_gym
SESSION_SECRET=replace-with-at-least-32-random-characters   # openssl rand -hex 32

S3_BUCKET_NAME=your-bucket
S3_REGION=your-region
S3_ENDPOINT=https://your-s3-compatible-endpoint
S3_ACCESS_KEY_ID=your-access-key-id
S3_SECRET_ACCESS_KEY=your-secret-access-key

Optional settings read by the code. Each optional capability stays off when its settings are missing or incomplete; strict mode logs a warning instead of failing.

VariableUsed for
NEXTAUTH_URLPublic site origin. Needed for Stripe return URLs and links in email. NEXT_PUBLIC_BACKEND_URL is read as a fallback.
INTERNAL_APP_URL, INTERNAL_GRAPHQL_URLPrivate origin for server-to-server GraphQL calls. INTERNAL_APP_URL wins when both are set.
CLIENT_SOURCE_TRUST_PROXYSet true only if your proxy appends the client address to X-Forwarded-For. Used by rate limits.
STOREFRONT_ORGANIZATION_IDBinds the public storefront to one organization. Required when more than one organization exists or when member signup is on.
PUBLIC_MEMBER_SIGNUPS_ALLOWED, PUBLIC_MEMBER_SIGNUP_ORGANIZATION_IDPublic member signup. Off by default. The signup organization must match STOREFRONT_ORGANIZATION_ID.
PUBLIC_SIGNUPS_ALLOWEDKeep false. Dashboard self-signup is refused either way; true only logs an error.
MAIL_ENABLED, SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD, SMTP_FROM, CONTACT_FORM_TOPassword reset and email-change mail, staff copies of contact messages. Mail also needs SMTP_HOST and SMTP_FROM. In production nothing is sent unless MAIL_ENABLED=true; outside production mail is on unless MAIL_ENABLED=false. Links in email use NEXTAUTH_URL.
STRIPE_ENABLED, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRETMembership checkout and counter card payments. STRIPE_ENABLED=true allows new charges. Checkout is hosted by Stripe, so no publishable key is read by the current code.
STRIPE_USE_ENVIRONMENT_PROVIDER, STRIPE_ACCOUNT_IDTake the Stripe provider for STOREFRONT_ORGANIZATION_ID from the environment. Onboarding copies STRIPE_ACCOUNT_ID onto the provider record it creates.
PAYMENT_TEST_MODE, PAYMENT_TEST_WEBHOOK_SECRETIsolated payment tests only. Strict mode refuses to start with PAYMENT_TEST_MODE=true.
QR_CODE_SECRETRotating member check-in QR codes (32+ characters). Without it, codes are unavailable.
KIOSK_API_TOKEN, KIOSK_ORGANIZATION_IDStaff-unlocked kiosk at /kiosk/check-in. Both are required.
DISCOVERY_API_KEY, DISCOVERY_ORGANIZATION_ID, DISCOVERY_API_SCOPES, DISCOVERY_PARTNER_ALLOWLISTEnvironment-configured partner discovery API. Credentials can also be issued from the Operations page.
OPENROUTER_API_KEY, OPENROUTER_MODEL, OPENROUTER_MAX_TOKENSDashboard AI chat (/api/completion).

Keep .env out of source control. Generate each secret separately.

Migrations and local run

npm run dev

npm run dev runs keystone build --no-ui, applies checked-in migrations with prisma migrate deploy, and starts Next.js. To change the schema, run npm run migrate:gen against a disposable database. It rebuilds Keystone, regenerates the dashboard view order, and runs keystone prisma migrate dev.

npm run dev applies migrations to whatever DATABASE_URL points at.

Once running:

  • Storefront: /
  • Member account: /account
  • Staff dashboard: /dashboard
  • Kiosk: /kiosk/check-in (after kiosk configuration and staff unlock)
  • GraphQL API: /api/graphql
  • Health check: /api/health (runs SELECT 1 against the database)

Other scripts: npm run lint, npm run typecheck, and npm run test:offline (runs tests/*.test.ts).

First operator

On an empty database, every route redirects to /dashboard/init. That page creates the first user with name, email, and password. The user is attached to the gym_default_organization organization, which the tenant migration creates, and gets an Admin role with full management capabilities.

Dashboard self-signup is refused. Add staff from the dashboard and give them a role. Members join through invitation (/dashboard/platform/members), contact import (/dashboard/platform/imports), or public signup when it is enabled.

Onboarding

Open the onboarding dialog from the dashboard sidebar. It offers three templates:

  • Complete Starter Setup (full): two membership plans, six class types, three instructors, fifteen weekly schedules.
  • Basic Starter Setup (minimal): one plan, one class type (Yoga), one instructor, three weekly schedules.
  • Custom Setup (custom): starts from the full configuration and lets you edit plans, classes, instructors, and schedules before it is validated.

All three also create (or reuse) store settings, one location, a bounded instructor role, and a Stripe provider record. The Stripe record is marked installed only when STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET are set. Setup creates no members, charges, subscriptions, or revenue.

After setup, work through the readiness checklist it returns. Required steps: store settings (name, contact email, hours), an open location, membership plans, class types, active coaches, and a weekly schedule with upcoming sessions. When checkout is on, at least one plan also needs a Stripe product and price ID.

On this page