Openfront Rental

Short-term accommodation listings, quotes, reservations, guest trips, host operations, calendar exchange, and payments.

Source · Live site · Catalog

Openfront Rental is a self-hostable app for people who manage short-term accommodation. Guests search stays, get a quote, reserve, sign a stay agreement, and follow a private trip link. Hosts use the dashboard for properties, listings, calendar, pricing, reservations, guests, inbox, tasks, payments, payouts, reports, channels, reviews, and store settings.

It is built with Next.js 16, React 19, Keystone 6, GraphQL, Prisma, and PostgreSQL. To install it, see Getting started.

Card payments use Stripe with sk_test_ and pk_test_ keys. Calendar exchange uses iCalendar text. Outbox events wait for a dispatcher you register in features/keystone/workers/outboxDispatchers.ts.

Concepts

ConceptMeaning
Host organizationThe tenant. Host-scoped records are visible through an active HostMembership
Property and unitThe building and the bookable AccommodationUnit inside it
ListingThe public page for a unit, with bookingMode instant or request
Rate planNightly rate, stay limits, notice, window, and a cancellation policy
QuoteA StayQuote snapshot of dates, rates, fees, taxes, deposit, and guests
ReservationA stay created from a quote
Trip linkA 48-character private token for the guest's trip, agreement, and messages

Guest and host surfaces

The storefront lives at / and is built from features/storefront. Its main routes are /search, /stays/[slug], /collections, /compare, /checkout/[quoteToken], /trips, /trips/[token], /agreements/[token], /messages/[token], /account, /help, and /policies. See External storefronts for the full table.

The host operator dashboard lives at /dashboard. Product pages are under /dashboard/platform and are built from features/platform. The generic Keystone list and item screens (/dashboard/[listKey]) come from features/dashboard. See External dashboards for the full table.

Models and tenancy

HostOrganization is the tenant root. HostMembership links a user to a host with a role (owner, admin, manager, operations, or finance) and a status (invited, active, suspended). Host-scoped records are visible to a user only through an active membership in the record's host. Users whose role has canManagePeople or canManageRoles are treated as platform administrators and see all hosts.

Role permissions come in read/manage pairs: properties, listings, calendar, pricing, reservations, guests, messages, payments, payouts, reports, channels, and reviews. Other flags are canAccessDashboard, canManageSettings, canManageOnboarding, canManagePeople, canManageRoles, canSeeOtherPeople, and canEditOtherPeople.

Current models in features/keystone/models:

AreaModels
People and tenancyUser, Role, HostOrganization, HostMembership, MarketplaceSettings
PropertiesProperty, PropertyAddress, PropertyOwner, PropertyOwnerAssignment, AccommodationUnit, Bedroom, Bed, Amenity
ListingsListing, ListingMedia, StayCollection, HouseRule, CalendarBlock
Pricing and policyRatePlan, SeasonalRate, FeeDefinition, TaxRule, CancellationPolicy
BookingStayQuote, GuestProfile, GuestParty, Reservation, ReservationAmendment, ReservationLineItem, StayAgreement
MoneySecurityDeposit, DamageClaim, Payment, PaymentProvider, PaymentWebhookEvent, Refund, Payout, OwnerStatement, OwnerStatementLine
Guest contact and workConversation, Message, Review, TaskRule, PropertyTask
Channels and jobsChannelConnection, ChannelListing, ChannelReservation, ChannelSyncEvent, DataExchangeJob, OutboxEvent

MarketplaceSettings is a single record for the public store identity: name, tagline, contact details, policy links, currency, locale, timezone, logo, accent color, and whether test bookings are allowed.

Quotes and reservation line items keep a snapshot of the accepted dates, rates, fees, taxes, deposit, guest counts, and currency. Later rate changes do not rewrite them.

Named GraphQL operations

Custom operations are merged in features/keystone/mutations/index.ts. The API is served at /api/graphql.

Public and guest

KindOperations
QueriespublicMarketplaceSettings, searchStays, publicListing, publicStayAvailability, publicStayQuote, publicTrip, publicStayAgreement, publicGuestThread, myGuestAccount, guestPasswordResetAvailable
BookingquoteStay, createStayReservation, recoverStayCheckout, requestStayCancellation, signStayAgreement
Card payment (test mode)startGuestStayPayment, completeGuestStayPayment
Guest account and messagesregisterGuestAccount, updateMyGuestProfile, changeGuestEmail, changeGuestPassword, openAccountTrip, recoverStayTrip, claimStayTrip, submitGuestInquiry, replyToGuestThread, sendTripMessage, submitStayReview

Public reads use exact slugs or opaque tokens. Trip, agreement, and message links use 48-character hex tokens.

Host

AreaOperations
ReservationstransitionStayReservation, reassignStayReservation, amendConfirmedStay, cancelStayQuote
Payments and refundsinitiateStayPayment, captureStayPayment, refundStayPayment
Deposits and claimstransitionSecurityDeposit, createStayDamageClaim, transitionDamageClaim
OwnersgenerateOwnerStatement, transitionOwnerStatement, ownerFinanceSummary
TaskscreateOperationsTask, transitionPropertyTask, runPropertyTaskRules
CalendarimportListingCalendar, exportListingCalendar
ReportshostOperationsReport, exportHostArchive, permittedHostOrganizations, listingReadinessIndex
OutboxdeliverOutboxEvents
OnboardingrentalOnboarding, updateOnboardingStatus, redirectToInit

hostStayCommand(command, input) is the single entry point most dashboard actions use. Its commands are: transition, amend, reassign, listing_allocate, party_amend, party_invites_amend, payment_record_manual, payment_cancel_attempt, refund_create, refund_record_manual, refund_process, refund_decline, refund_assess, deposit, claim_create, claim_transition, agreement_void, agreement_reissue, conversation_reply, conversation_status, conversation_link_revoke, conversation_link_reissue, review_moderate, and catalog_transition. It requires a signed-in user and returns { ok, message, data }.

Generated Keystone CRUD still exists for every model. Reservation, payment, and refund state should change through these commands, not through raw updates.

Onboarding

Onboarding runs from the dashboard. It calls rentalOnboarding with mode set to create (one item at a time), verify, or complete. The caller needs canManageOnboarding. Each item runs in a serializable transaction and is created or reused, so a second run does not duplicate records.

There are three templates:

  • Minimal: one home (Pineglass) with its amenities, collection, one guest, and one stay.
  • Full: the whole Housekind portfolio from features/platform/onboarding/lib/seed.json. That is store settings, the Housekind Collective host, amenities, collections, five properties with units and listings, a cancellation policy, house rules, rates, calendar blocks, guests, reservations, tasks, owner statement and payout records, a manual payment provider, a Stripe card provider record, and one availability-only iCalendar connection.
  • Custom: the full template merged with your own JSON, validated by a schema.

The final step checks readiness for records that onboarding never creates, such as quotes, amendments, damage claims, refunds, webhook events, channel reservations, and outbox events. Those only come from real activity.

npm run seed:runtime is a separate local script. It refuses to run unless DATABASE_URL points to a local database named runtime_rental.

Integrations

Payments

Adapters live in features/integrations/payment:

CodeWhat it does
pp_system_defaultManual. A host records money already received and attests manual refunds.
pp_stripe_stripeStripe card payments in test mode only. Enabled when STRIPE_ENABLED=true and the keys start with sk_test_ and pk_test_. Needs STRIPE_WEBHOOK_SECRET. Card checkout is offered only for test bookings.
pp_signed_testWebhook-only provider for testing signed callbacks with PAYMENT_WEBHOOK_SIGNED_TEST_SECRET. It cannot create payments or refunds.

Webhooks arrive at /api/payment-webhooks/[providerCode]. The route limits body size and read time before verifying the signature.

Calendar channels

Adapters live in features/integrations/channels. Two are installed: direct and ical. The ical adapter imports and exports availability only. It does not sync rates, reservations, or messages, and it has no remote transport: hosts paste calendar text into importListingCalendar and fetch output from exportListingCalendar.

Mail

Password reset and guest mail use SMTP (SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD, SMTP_FROM, SMTP_STORE_LINK). Guest password reset is offered only when host, sender, and store link are set.

Outbox

OutboxEvent records are written for later delivery. npm run outbox:deliver runs the worker, but no dispatcher is installed. Runs report no_dispatcher and leave events as they are until you add one in features/keystone/workers/outboxDispatchers.ts.

AI assistant and MCP

/api/completion powers the dashboard assistant through OpenRouter (OPENROUTER_API_KEY, OPENROUTER_MODEL, optional OPENROUTER_MAX_TOKENS). /api/mcp-transport/[transport] exposes MCP tools that forward the caller's session cookie to GraphQL: listModels, queryData, searchModels, getFieldsForType, lookupInputType, createData, updateData, deleteData, modelSpecificSearch, and stayCommand. stayCommand runs a subset of host commands (transition, conversation_reply, conversation_status, review_moderate, catalog_transition, refund_decline, payment_cancel_attempt, payment_record_manual) and requires confirm: true.

On this page