|

Getting started with Scheduling

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

Live demo

A hosted demo runs at scheduling.openship.org. Use it to see the public booking pages and the operator dashboard before you install anything. Demo data is fictional.

Requirements

  • Node.js 20.9 or later (engines.node is >=20.9.0; the README targets Node.js 22).
  • A PostgreSQL database you are allowed to migrate.
  • npm, using the checked-in package-lock.json.

Clone and install

git clone https://github.com/openshiporg/openfront-scheduling.git
cd openfront-scheduling
npm ci

Environment

Copy .env.example to .env and replace every placeholder. The minimum for local development is a database and a session secret:

DATABASE_URL=postgresql://user:password@localhost:5432/openfront_scheduling
SESSION_SECRET=replace-with-output-of-openssl-rand-hex-32

In local development, several feature secrets fall back to a key derived from SESSION_SECRET. Set them explicitly before any shared or production use, and do not reuse one value for several of them:

VariableUsed for
BOOKING_MANAGE_SECRETSigning guest booking management links (at least 32 bytes)
RATE_LIMIT_SECRETPublic request rate-limit buckets (at least 32 bytes)
TEAM_INVITATION_SECRETTeam invitation tokens (at least 32 bytes)
MACHINE_TOKEN_PEPPERHashing API keys and OAuth client secrets
ACCOUNT_EMAIL_CHANGE_SECRETCustomer email-change confirmation tokens
SCHEDULING_PUBLIC_URLPublic HTTPS origin used in emails, invitations, manage links, and MCP. Falls back to NEXTAUTH_URL, then RAILWAY_PUBLIC_DOMAIN
STOREFRONT_TEAM_SLUGOptional. Which team the public storefront shows; defaults to the oldest active team with public booking turned on
PUBLIC_SIGNUPS_ALLOWEDtrue opens the dashboard sign-up page; keep false unless you want open registration
TRUSTED_PROXY_HOPSNumber of trusted proxy hops for reading client IPs from forwarded headers (0 to 10)

Object storage (S3)

Keystone is configured with an S3-compatible image store named my_images. It reads these variables, with test defaults when unset:

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

No current list has an image or file field that uses this store (team logos are sanitized inline SVG), so storage is optional today. The production configuration rules in features/keystone/lib/productionConfig.ts expect all of the first four once any S3 variable is set, and an HTTPS S3_ENDPOINT.

Optional integrations

Email (SMTP), calendar OAuth, credential encryption keys, SMS, and AI chat have their own variables. They are listed in Integrations and boundaries. Keep every provider secret out of source control.

Migrations and local run

The repository has 44 checked-in Prisma migration directories under migrations/.

npm run migrate      # prisma migrate deploy
npm run dev          # keystone build --no-ui, npm run migrate, next dev

npm run dev and npm run build both run npm run migrate against DATABASE_URL. npm run migrate:gen runs prisma migrate dev, which can reset a database. Use a disposable development database for migration generation and never point these scripts at a database you cannot rebuild.

After start-up:

  • Public booking pages: /
  • Operator dashboard: /dashboard
  • GraphQL API: /api/graphql

First admin and onboarding setup

  1. On an empty database, visit /dashboard/init and create the first user. That user gets an Admin role with dashboard, scheduling, onboarding, people, and role permissions.
  2. Open the dashboard. The onboarding dialog calls bootstrapSchedulingOnboarding, which requires the canManageOnboarding role flag and an idempotency key.
  3. Pick a mode:
    • Demo creates the fictional Northstar Studio team from features/platform/onboarding/lib/seed.json: two members, a weekly schedule, two event types, three booking questions, a routing form with two rules, an embed, a workflow reminder, a notification subscription, a webhook subscription, a manual payment provider, a placeholder Google calendar connection in needs_reauth, and two sample bookings.
    • Merchant creates your own team from a business name, slug, IANA time zone, owner display name, and SVG logo. Sample data is opt-in.
  4. Re-running the same request replays the result rather than creating duplicates.
  5. Connect calendars, conferencing, and payment providers from /dashboard/platform/integrations before you enable event types that depend on them.

Source checks

npm test             # tsx --test tests/*.test.ts
npm run lint         # eslint .
npm run audit:relations

Passing checks cover only the paths they exercise. They do not prove provider credentials, delivery, or payment behavior in your environment.

Deployment

The app is one Next.js server plus PostgreSQL. Optional pieces are SMTP, S3-compatible storage, and the external providers you choose.

  • Build: railway.toml uses keystone build --no-ui && next build. The package build script also runs npm run migrate; the Railway config moves migration to start time because the build environment cannot reach the database.
  • Start: railway.toml runs npm run migrate && next start. Alternatively, npm run start:production (scripts/start-production.sh) applies migrations, runs a scheduling outbox loop every SCHEDULING_WORKER_INTERVAL_SECONDS (default 30), and starts Next.js.
  • Background work: in production, instrumentation.ts also runs the scheduling outbox inside the Next.js server every 30 seconds. It processes webhooks, reminders, calendar writes and syncs, payments, conferencing, workflows, data jobs, invitations, analytics, and expiries. Workers claim rows with locks, so several replicas can run side by side. npm run worker:scheduling-outbox runs a single pass.
  • Environment: set DATABASE_URL, SESSION_SECRET, BOOKING_MANAGE_SECRET, RATE_LIMIT_SECRET, TEAM_INVITATION_SECRET, SCHEDULING_PUBLIC_URL (HTTPS), SMTP_STORE_LINK (HTTPS base for password reset links), and SMTP_HOST, SMTP_PORT, SMTP_FROM (plus SMTP_USER and SMTP_PASSWORD if your server needs auth) so invitation, reminder, and reset emails can be sent. productionConfig.ts lists these rules, but current source does not call its check at start-up, so verify the environment yourself. Add calendar, payment, and conferencing keys only for providers you use. In production, calendar, payment, and conferencing credential encryption refuses to run without its dedicated key.
  • Postgres and S3: use a managed PostgreSQL instance with backups. Configure S3 only if you add file or image fields.

The README also includes a Vercel deploy button that prompts for DATABASE_URL and SESSION_SECRET. On a serverless host, run migrations as a separate step and run the outbox worker somewhere long-lived, because the in-process loop needs a persistent Node.js server.

On this page