Getting started

Clone Openfront Restaurant, configure PostgreSQL, sessions, and S3 storage, apply migrations, create the first admin, run onboarding, and deploy.

This guide sets up one restaurant on your own machine or server. Onboarding creates sample setup records: store settings, menu, modifiers, kitchen stations, floors, sections, tables, and payment provider records. It does not create provider accounts or prove that payments, delivery zones, or staff permissions are ready for service.

Hosted app

The hosted app is at restaurant.openship.org. The operator dashboard is at /dashboard on the same host.

Requirements

  • Node.js 20 or newer (package.json sets engines.node to >=20.0.0).
  • npm. The repository ships a package-lock.json.
  • A PostgreSQL database you are allowed to migrate. DATABASE_URL must start with postgresql:// or postgres://.
  • S3-compatible object storage for menu images. It is required in production.

Install

git clone https://github.com/openshiporg/openfront-restaurant.git
cd openfront-restaurant
npm install

The README mentions cp .env.example .env, but the repository does not include a .env.example file. Create .env yourself.

Environment

features/keystone/runtimeConfig.ts reads the core settings. When NODE_ENV is production, DATABASE_URL, SESSION_SECRET, and all five S3_* values are required and the app will not start without them. Outside production, the code falls back to a local database URL, a development session secret, and placeholder storage values. SESSION_SECRET must be at least 32 characters. The same secret signs the storefront cart proof cookie.

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

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-compatible-endpoint

Optional settings read by the code:

VariableUsed for
STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRETStripe adapter and webhook signature checks
NEXT_PUBLIC_STRIPE_KEY (falls back to STRIPE_PUBLISHABLE_KEY in some server code)Stripe card form in storefront checkout and POS
NEXT_PUBLIC_PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET, PAYPAL_WEBHOOK_IDPayPal adapter and webhook verification
NEXT_PUBLIC_PAYPAL_SANDBOXPayPal sandbox mode. Sandbox is used unless this is false
OPENROUTER_API_KEY, OPENROUTER_MODEL, OPENROUTER_MAX_TOKENSShared keys for the dashboard AI assistant
SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD, SMTP_FROMPassword reset email, order notifications, and campaigns
SMTP_STORE_LINKBase link used in password reset emails
MAIL_USEROnly checked to log Ethereal test-mail preview links
PUBLIC_SIGNUPS_ALLOWEDSet to true to open /dashboard/signup
NEXT_PUBLIC_BACKEND_URLPoints the storefront GraphQL client at another host. Defaults to the current origin
NEXT_PUBLIC_VIEW_ORDERFallback dashboard field view order when the generated file is missing

Keep .env out of source control.

Migrations and local run

npm run dev

npm run dev runs keystone build --no-ui, applies the checked-in migrations with prisma migrate deploy (npm run migrate), 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. Commit model changes and migrations together.

npm run dev and npm run build both apply migrations to whatever DATABASE_URL points at.

Once running:

  • Storefront: http://localhost:3000/
  • Operator dashboard: http://localhost:3000/dashboard
  • GraphQL API: http://localhost:3000/api/graphql

The README lists /pos and /kds routes. Those do not exist. The POS is at /dashboard/platform/pos and the KDS is at /dashboard/platform/kds.

First user

When the database has no users, every request redirects to /dashboard/init. Create the first user there with a name, email, and password. That user gets an Admin role with every permission flag turned on. Then sign in at /dashboard/signin.

Dashboard sign-up at /dashboard/signup stays closed unless PUBLIC_SIGNUPS_ALLOWED=true. Password reset email only works when SMTP_HOST is set.

Onboarding

On a fresh install the dashboard sidebar shows an onboarding card ("Welcome to Openfront Restaurant"). It opens a dialog with three templates:

TemplateWhat it creates
Complete Restaurant SetupStore settings for "Stack Kitchen", 6 categories, 23 menu items, 11 modifiers, 6 kitchen stations (Grill, Fryer, Salad, Bar, Dessert, Prep), 1 floor, 2 sections (Main Dining, Patio), 8 tables, and 3 payment methods
Basic MenuA small subset: 3 categories, 3 items, 4 modifiers, 3 stations, 1 floor, 1 section, 2 tables, Cash and Credit Card
Custom SetupYour own JSON for each section

The seed data lives in features/platform/onboarding/lib/seed.json. Records are written through the ensureRestaurantOnboardingRecord mutation, which checks the permission for each record type and only accepts known fields.

The three payment methods map to provider records:

Onboarding nameProvider recordCode
Credit CardStripepp_stripe_stripe
Mobile PaymentPayPalpp_paypal_paypal
CashManual / Cashpp_system_default

The seeded store has delivery turned off and an empty postal code allowlist. After onboarding, the dialog shows a readiness checklist (menu prices and modifiers, service hours and delivery zone, payment and refund flow, staff permissions). Every item is marked "Not verified". The app does not check them for you.

Onboarding status is stored on the user (not_started, in_progress, completed, dismissed).

Deploy

Scripts in package.json:

ScriptEffect
npm run buildkeystone build --no-ui, npm run migrate, then next build
npm run migrateprisma migrate deploy
npm startnext start
npm run lintESLint
npm run test:offlineNode test runner over tests/*.test.ts

The included railway.toml builds with keystone build --no-ui && next build and starts with npm run migrate && next start, because the build step cannot reach an internal database. The README also has a Vercel deploy button that asks for SESSION_SECRET and a Postgres database.

Either way, provision PostgreSQL, S3-compatible storage, and any SMTP and payment credentials yourself. All app processes must share the same database, SESSION_SECRET, and storage. There is no background worker in the source. Notifications, campaigns, webhook retries, and recovery jobs are run by operators from the dashboard.

Where to go next

On this page