Repository structure and conventions
Status: accepted for the first scaffold, 26 October 2026. It changes through pull requests that update this file and, for decisions, an ADR in docs/adr.
The reference architecture is the technical analysis in docs/GuestCraft-Technical-Analysis.pdf. Its chapter 4 covers the architecture, chapter 18 the engineering rules and chapter 20 the phases. This file turns those chapters into a repository layout with rules that CI checks. Section 9 says honestly what each check can and cannot prove.
1. Goals
Section titled “1. Goals”- One codebase, two runtimes. The same packages run on Vercel (Next.js with the API mounted inside) and in Docker (Next.js standalone plus a resident worker). PostgreSQL is the only mandatory service.
- Boundaries that hold. The domain never imports infrastructure. Only the composition root wires adapters to use cases. MIT code never depends on AGPL code.
- One reason per package. A package exists because of a dependency boundary, a license boundary or a distribution boundary. Packages are never created per feature, per bounded context or to satisfy a coverage threshold, and they appear only with their first real code.
- Fast, honest feedback. Type checking and unit tests run in seconds. Database behaviour (RLS, locks, constraints) is tested on a real PostgreSQL 17, never on an in-memory imitation.
2. Layout
Section titled “2. Layout”guestcraft/├─ apps/│ ├─ web/ # Next.js: the screens and the HTTP bridge to server/http under /api│ ├─ mobile/ # Expo app that connects to any instance (MIT, ADR 0041)│ └─ docs/ # documentation website over docs/, published to GitHub Pages├─ packages/│ ├─ core/ # domain model, use cases and ports, pure TypeScript│ ├─ db/ # the whole SQL schema, migrations, RLS, role bootstrap, unit of work, repositories│ ├─ api/ # Hono routes, OpenAPI 3.1, RFC 9457 errors│ ├─ server/ # composition root: http, worker, migrate and config entry points│ ├─ auth/ # Better Auth adapter and GuestCraft access control│ ├─ jobs/ # job catalog, pg-boss worker, schedules, budgeted drain, dead letters│ ├─ channex-client/ # typed HTTP client for the Channex API (MIT)│ ├─ channex/ # Channex adapter: sync engine ports implemented on the client│ ├─ channex-simulator/ # stateful Channex simulator for tests and local development (MIT)│ ├─ identity-provider-simulator/ # OIDC and SAML identity provider simulator for SSO tests (MIT)│ ├─ stripe-simulator/ # stateful Stripe simulator for tests and local development (MIT)│ ├─ stripe/ # payment-provider adapter: the core PaymentProvider port on Stripe's HTTP API│ ├─ countries/ # country packs: a fictitious test country and Italy│ ├─ storage/ # object storage adapters: S3-compatible buckets and private Vercel Blob│ ├─ sdk/ # GuestCraft TypeScript SDK (MIT)│ ├─ react/ # TanStack Query hooks over the SDK (MIT)│ ├─ mcp/ # MCP server over the public API, stdio and Streamable HTTP (MIT)│ ├─ tokens/ # design tokens in DTCG format (MIT)│ ├─ ui/ # web components of the design system│ ├─ i18n/ # ICU messages shared by web and mobile (MIT)│ ├─ testing/ # generic test helpers, independent of product packages (MIT)│ └─ tsconfig/ # TypeScript presets per environment (MIT)├─ tooling/│ ├─ scripts/ # CI and maintenance scripts, shared test setup, the analyzers' fixtures│ ├─ codegen/ # isolated code generation workspace with the TypeScript 6 API│ └─ biome/, spectral/, quality/, requirements/ # lint rules, OpenAPI rules, budgets, tool versions├─ docs/│ ├─ adr/ # architecture decision records│ ├─ architecture/ # this file, its reviews and other living design notes│ ├─ developer/ # installation, operation and the workflow contracts│ ├─ product/ # the product status│ └─ analysis/ # sources of the technical analysis├─ Dockerfile, docker-compose*.yml # the web, worker and migrate images and the reference runtime└─ openapi/ # the generated GuestCraft OpenAPI document, versioned (MIT)The tree lists what exists. The dependency matrix also reserves two rows for planned packages: apps/worker, a host for server/worker (today the Docker worker image runs server/worker directly), and packages/webhooks, MIT Standard Webhooks primitives (today server/http signs deliveries and the SDK’s webhooks module verifies them). packages/cli, packages/fake and packages/apps are planned and arrive with their first real code; e-mail and outbound delivery adapters join packages/storage, the backend integration package, when they are written. Acceptance tests of the composed runtime live in packages/server/test/integration, and checks of the analyzers and artifacts in tooling/scripts/test/integration. The analysis puts the Channex simulator under tools/; here it is a package, because tests in several packages and the end-to-end server import it.
3. Packages
Section titled “3. Packages”Dependencies in this table are internal production imports. External libraries, peer dependencies, presets and test tools are declared per package and checked separately.
| Package and entry points | License | Responsibility | Internal imports allowed |
|---|---|---|---|
core/shared, core/<context> |
AGPL-3.0, private | Domain calculations, use cases, ports (including the unit of work), domain events and error contracts. No I/O. | none |
db, db/auth, db/scheduler, db/migrations, db/bootstrap |
AGPL-3.0, private | The single owner of GuestCraft’s SQL: schema, migrations (pg-boss’s schema among them), RLS, table inventory, role bootstrap, the unit of work and repositories implementing core ports. |
core |
auth |
AGPL-3.0, private | Better Auth adapter, identity-to-principal mapping, the guarded /api/auth/** handler, API key scopes and workspace access (ADR 0017). No migration journal of its own. |
core, db/auth |
jobs |
AGPL-3.0, private | Typed job catalog, the pg-boss worker and budgeted drain, schedules, recovery and the dead letter queue (ADR 0019). Enqueueing joins the current unit of work. The only package that imports pg-boss. | core, db/scheduler |
channex-client |
MIT, published later | Portable Channex client with its own validated wire contracts. Knows nothing about GuestCraft. | none |
channex |
AGPL-3.0, private | Channel-manager adapter; sync orchestration stays in core use cases. |
core, channex-client |
channex-simulator |
MIT, published later | Independent state machine of Channex and scenario oracle, in process and over HTTP. | channex-client, type-only |
identity-provider-simulator |
MIT, private | Specification-driven OpenID Connect provider and SAML 2.0 identity provider for single sign-on tests, in process and over HTTP. Imported only by test and setup files; never imports the SSO, SAML or JOSE libraries it tests. | none |
stripe-simulator |
MIT, private | Independent state machine of the Stripe endpoints GuestCraft uses, in process and over HTTP | none |
stripe |
AGPL-3.0, private | Payment-provider adapter: the core PaymentProvider port on Stripe’s HTTP API |
core |
countries/<id> (later countries/<id>/adapters), countries/conformance |
AGPL-3.0, private | Country packs (ADR 0015): each country’s rules as dated data with their sources, behind the contract of core/countries, and the conformance kit. Protocols and portals are planned, in separate adapter subpaths. |
core |
storage |
AGPL-3.0, private | Object storage adapters behind the ObjectStore port of core/files: S3-compatible buckets (AWS S3, Cloudflare R2, MinIO, Supabase Storage) and private Vercel Blob stores (ADR 0046). |
core |
api |
AGPL-3.0, private | Pure Hono route and schema factory, middleware, error serialization, offline OpenAPI export (ADR 0016). Receives use cases and the auth handler as interfaces. | core, i18n (the texts of problems) |
server/http, server/worker, server/migrate, server/config |
AGPL-3.0, private | The only composition root, one entry point per runtime (ADR 0010). | core, db, auth, jobs, channex, channex-client, stripe, countries, storage, api, i18n, mcp |
webhooks (planned) |
MIT, published later | Standard Webhooks primitives; runtime-specific crypto behind explicit subpaths. Until it exists, server/http signs deliveries in its own webhooks module and sdk verifies them in its own; no package imports it. |
none |
sdk |
MIT, private until release | Generated client plus the hand-written transport, pagination, errors and webhook verification. It never retries a request. | none |
react |
MIT, private until release | Query factories and hooks; React and TanStack Query are peers. | sdk |
mcp |
MIT, private until release | The MCP server: tools and resources generated from the instance’s OpenAPI document and composed front-desk tools, called with the caller’s own identity over stdio or Streamable HTTP, with a server-enforced confirmation of destructive tools. A client of the public API only; server/http serves it at /api/mcp. |
sdk |
tokens |
MIT, published when needed | Platform-neutral design data. | none |
i18n |
MIT, published when needed | Localization on the API’s wire formats (ADR 0012): language negotiation, formatting of money, dates and measures, reading of numbers people type, and the ICU catalogs with their linter and pseudo-locales. | none |
ui |
AGPL-3.0, private | Web design system; React and its UI libraries are peers. | tokens, i18n |
testing |
MIT, private | Generic clocks, random sources, fixture utilities, the PostgreSQL lifecycle with an injected migration step, PgBouncer in transaction mode, and MinIO for object storage. Imported only by test and setup files. | none |
tsconfig |
MIT, private | Presets per environment, all with the same strictness. | none |
apps/web |
AGPL-3.0, private | Hosts server/http in Next.js; the dashboard uses sdk, react and ui, plus the Better Auth client for identity and session flows only. |
server/http, sdk, react, ui, tokens, i18n |
apps/worker (planned) |
AGPL-3.0, private | Starts and stops server/worker; never imports Next.js. |
server/worker |
apps/mobile |
MIT, private | The Expo app for any instance: discovery, device sign-in, Today and housekeeping (ADR 0041). Its versions are the mobile pnpm catalog; Jest, not Vitest, tests it. |
sdk, i18n, tokens |
apps/docs |
AGPL-3.0, private | The documentation website: Astro Starlight over the repository’s Markdown, read in place, and the API reference of openapi/v1.json (README). |
tokens |
AGPL-3.0 means AGPL-3.0-only in the SPDX headers, provisionally (ADR 0004). GuestCraft’s OpenAPI document, its schemas and the code generated from them are MIT.
4. Dependency rules
Section titled “4. Dependency rules”- Separate graphs. Production imports, test and setup imports, and package manifests are checked separately. Development dependencies count for package cycles; a test-only edge never makes a production import legal.
- Default deny. An internal import is allowed only if section 3 allows it. Unresolved imports, imports outside another package’s
exports, relative imports that cross a package, undeclared npm dependencies, devDependencies imported by shipped code, andimport typeedges are all checked. Some subpaths are narrower than their package:db/migrationsanddb/bootstraponly forserver/migrate,db/authonly forauth,db/scheduleronly forjobsandserver,server/migratefor no runtime, and each app only its own runtime ofserver. A workspace package is any package ofpnpm-workspace.yaml, and each needs a row in the matrix. - The core is pure.
coreimports no workspace package and no Node.js built-in. Insidecore, a bounded context reaches another only through itsindex.ts, andcore/sharedimports no context. Ambient I/O (fetch, timers,Date,Temporal.Now,Math.random,Intl,process) is rejected incoreby lint rules, because an import graph cannot see it, and its sources are type-checked without Node.js types. - One unit of work. Tenant data is reached only through the unit of work, which binds repositories, events, the outbox and job enqueueing to one transaction with the tenant context set locally (ADR 0009). Cross-tenant lookups use separate, named, narrowly privileged capabilities.
- One schema owner, one migration command. Only
dbdefines tables; onlyserver/migratemigrates; runtimes never hold migration credentials (ADR 0010). - Import-safe composition. No module does work when imported. Each runtime initializes explicitly and owns its disposal.
- License direction. An MIT package never imports an AGPL package, not even a type. Published MIT packages are also checked as packed tarballs, outside the monorepo.
- The simulator is independent.
channex-simulatormay import types fromchannex-client, never its validators, transport or helpers: it must not share the client’s assumptions.stripe-simulatorimports no workspace package at all. - Tests stay on their side. Test files, fixtures,
packages/testingand the simulators (channex-simulator,stripe-simulator,identity-provider-simulator) are reachable only from test and setup files. Tests may sit insrc/next to the code; their file names (*.unit.test.ts,*.property.test.ts,*.integration.test.ts,*.component.test.tsx) mark them as test code.
5. Package anatomy
Section titled “5. Package anatomy”packages/<name>/├─ package.json # "@guestcraft/<name>", "type": "module", explicit "exports", "license"├─ tsconfig.json # extends a preset from @guestcraft/tsconfig├─ vitest.config.ts # unit and property tests: defineProject with name and include├─ vitest.integration.config.ts # integration tests, when the package has them├─ README.md # purpose, public API, how to test├─ src/│ ├─ <entry>/index.ts # one index per subpath export│ ├─ **/*.ts│ └─ **/*.unit.test.ts, **/*.property.test.ts└─ test/ ├─ integration/*.integration.test.ts └─ fixtures/Every package that contains code declares the scripts typecheck and test (tooling/scripts has only integration tests, and packages/tsconfig has no code); packages with integration tests also declare test:integration. The core type-checks its sources and its tests as two programs, so that its sources never see Node.js types. pnpm test runs the fast suites without Docker. pnpm test:coverage runs everything in one Vitest process with coverage, and it is the command CI uses.
6. TypeScript and modules
Section titled “6. TypeScript and modules”- ESM only, Node.js 24 LTS, TypeScript 7 in strict mode with
noUncheckedIndexedAccess,exactOptionalPropertyTypes,noImplicitOverrideandnoImplicitReturns. - Presets per environment, one strictness.
base(no platform globals, forcore),node,react(the DOM and JSX, forui),next,expo(Metro’s resolution forapps/mobile); alibrarypreset for published packages is planned. A consumer type-checks the sources it imports with its own options, so every preset must accept the whole internal source graph. Private source consumers do not set a narrowrootDir. - Source-first internal packages.
exportspoint tosrc/*.ts; Vitest, Next.js and Node.js type stripping read the sources directly. No package is published yet; a publishable package will add a build and swap its entry points inpublishConfig. - Only erasable syntax: no enums, namespaces or parameter properties. Relative imports carry the
.tsextension. - No
isolatedDeclarationsby default: it rejects ordinary Zod, Drizzle and Hono inference (review finding R01). Public functions still declare their types by convention. - Wire formats are normalized at the boundary. Zod’s optional fields allow
undefined; domain types withexactOptionalPropertyTypesdo not. The conversion happens where data enters, explicitly. - The compiler API is isolated. Tools that need it receive TypeScript 6 through
packageExtensions; the project compiler stays TypeScript 7 (ADR 0003).
7. Runtimes, artifacts and the contract pipeline
Section titled “7. Runtimes, artifacts and the contract pipeline”server/httpbuilds the Hono app (API plus the guarded auth handler);apps/webmounts it and owns everything specific to Next.js.server/workerbuilds the job runner and the scheduler, and itsmain.tsstarts it and handles signals.server/migrateis a tooling entry point.server/configparses the environment for the runtime being started, never at import time.- The worker and migrate images run the TypeScript sources of a
pnpm deployofserver, pruned bytooling/scripts/src/prune-runtime.tsto what their entry points load, with Node.js type stripping. The Next.js standalone output declares the workspace root. CI starts each artifact from an isolated directory. - The OpenAPI pipeline is one-way: route and schema registration in
api→ offline export toopenapi/v1.json(no database, no auth, no worker) → generation ofsdkintooling/codegen→ type checks and tests of the packed SDK in isolated consumers. - Ownership of planned work: email and outbound delivery adapters join the object storage adapters in
storage, the one backend integration package; logging and tracing setup intoserver, Next.js instrumentation intoapps/web; environment kill switches intoserver/configand the unit of work.
8. Naming
Section titled “8. Naming”| What | Convention | Example |
|---|---|---|
| Files | kebab-case | availability.ts, revision-feed.ts |
| Fast tests | next to the code | availability.unit.test.ts, availability.property.test.ts |
| Integration tests | in test/integration |
rls.integration.test.ts |
| Types and classes | PascalCase, no I prefix |
RatePlan, ChannexClient |
| Identifiers | TypeID with a registered prefix, stored as UUIDv7 | rsv_01k8…, type TypeId<"rsv"> |
| Error codes | dotted, lowercase, stable | inventory.capacity_exceeded |
| Codes of the world | ISO 3166-1 alpha-2, ISO 4217, canonical BCP 47 tags, CLDR unit names | IT, EUR, pt-BR, square-meter |
| Message keys | the namespace, then the problem code or the screen, dotted | problems.money.invalid_amount.title, common.nights |
| Database | snake_case, plural tables | inventory_days, workspace_id |
| Environment variables | GUESTCRAFT_ prefix, except standard names |
GUESTCRAFT_CRON, DATABASE_URL |
9. Checks, and what they prove
Section titled “9. Checks, and what they prove”| Check | Proves | Does not prove |
|---|---|---|
dependency-cruiser (pnpm boundaries) |
the import structure of sections 3 and 4, including type-only edges and npm packages; a canary edge proves the parser works; every workspace package has a row | that code has no I/O, that SQL respects table ownership; undeclared imports of workspace packages are caught by resolution, not by this rule |
| Biome with core overrides | no direct use of ambient I/O, process.env or Node.js modules where forbidden |
indirect access through aliases of globals |
pnpm licenses:check and reuse lint |
declared license direction, private AGPL packages, a license for every file | the licenses of a packed tarball (checked before publishing) |
| Type checks with the strict presets | type correctness of every package with its consumers’ options; no Node.js types in the core’s sources | runtime behaviour |
| Tests with coverage on PostgreSQL 17 | behaviour, RLS matrix, constraints, privileges, isolation through a transaction pooler | that the tests are meaningful: mutation testing measures that |
The analyzers have fixtures that they must reject, in tooling/scripts/test/integration, so that disabling a rule cannot turn the gate green silently: dependency-cruiser on a workspace of deliberate violations; Biome’s core rules on a probe file; the license check on a workspace of bad manifests; the core’s source program, listed without Node.js types; the coverage thresholds, pinned to ADR 0005. Knip and REUSE have none: their configuration is small, and CI runs them on every pull request.
10. Where does a change go?
Section titled “10. Where does a change go?”| Change | Packages |
|---|---|
| A new API endpoint | route and schemas in api, use case in core, repository in db, wiring in server/http, regenerated openapi/ (pnpm --filter @guestcraft/api openapi) and SDK (ADR 0016) |
| A new problem code | its status in PROBLEM_STATUS of api, its title and detail in the catalogs of i18n; the tests of api find codes without either |
| A new Channex operation | schema, method and tests in channex-client; behaviour in channex-simulator; use in channex |
| A new domain rule | core, with unit and property tests |
| A new table | migration with its RLS policy, inventory entry and Drizzle definition in db (ADR 0011); the catalog and RLS tests cover it |
| A new country | a subpath of countries with a manifest, dated and sourced rules and their tests, registered in the composition root; the conformance kit checks it (ADR 0015) |
| A new language | its catalog in i18n/catalogs (pnpm i18n:catalogs writes the runtime files), its loader and CATALOG_LANGUAGES entry (or GUEST_LANGUAGES for guest pages), the CLDR area patterns regenerated (pnpm i18n:cldr); the compiler and the tests check it |
| A new message | its English text and description in i18n/catalogs/en.json, then pnpm i18n:catalogs; the application ships English only, and translation in Weblate is planned (ADR 0013) |
| A new design token or colour | the data in tokens/src, with every new colour pair in CONTRAST_PAIRS; css:build in tokens and theme:build in ui regenerate the stylesheets (ADR 0018) |
| A new web component | ui, on React Aria, with its messages in the ui namespace of the catalogs, axe and keyboard tests for each state, and a place on the page of the style test |
| A new job | catalog entry and handler in jobs, scheduling in server/worker (ADR 0019) |
| A new version of pg-boss | the catalog version, then pnpm --filter @guestcraft/db scheduler:migration writes the migration of its schema (ADR 0019) |
11. Capabilities and where they live
Section titled “11. Capabilities and where they live”Each capability below names the packages that implement it, what works and what is still partial or planned. The product status is the operator-facing summary; the ADRs keep each decision and its history; the developer guides are the workflow contracts.
Runtimes and deployment
Section titled “Runtimes and deployment”server/config validates the settings of the runtime being started; server/http composes the API, the guarded auth handler and first-run setup; server/worker runs the jobs and the scheduler; server/migrate is the only privileged step. apps/web mounts the HTTP handler under /api and renders the screens. Docker Compose runs the web, worker and migrate images; on Vercel, production builds migrate, previews never do, and authenticated requests and a daily cron wake bounded job pumps. Migrations follow expand/contract, so a runtime accepts a newer journal whose extra migrations are all expand. Release automation (images with provenance and SBOM attestations) is in place, but no version has been released yet. See ADRs 0010, 0020, 0037, 0038 and 0039, and the installation, one-click deployment and releasing guides.
Public API, OpenAPI and clients
Section titled “Public API, OpenAPI and clients”api registers every operation with its schemas, permission and exact problem list; the offline export writes openapi/v1.json; tooling/codegen generates the SDK’s types and Zod validators; sdk adds the transport, pagination, errors and webhook verification; react adds the query factories. Cookie sessions, device tokens, workspace API keys, organization tokens and client sessions authenticate requests; auth/integration-credentials.ts is the one gateway of the integrations’ credentials. mcp turns the instance’s OpenAPI document into MCP tools that call the API through sdk, over stdio and Streamable HTTP; server/http serves it at /api/mcp, calling the API in process with each caller’s principal. Partial: the SDK, the React package and the MCP package are not published. See ADRs 0016, 0022 and 0047, the HTTP API, the SDK and the MCP server.
Identity, workspaces, team and roles
Section titled “Identity, workspaces, team and roles”auth adapts Better Auth for identities and sessions. Workspaces, memberships, custom roles, the permission matrix (core/tenancy), property-scoped access, invitation links and the audit log stay in GuestCraft (db, db/auth). The Settings screens manage users, roles, property access, activity (which names an API key and its creator for actions taken with a key), the account, and the transfer of the organization’s ownership to another administrator. See ADRs 0017, 0030 and 0036, and roles and property access.
Properties, room types and rooms
Section titled “Properties, room types and rooms”core/properties owns properties with their time zone and currency; core/reservations owns room types with occupancy limits and physical rooms, which are archived and restored. See properties and the front desk.
Reservations, availability and the front desk
Section titled “Reservations, availability and the front desk”core/reservations holds the booking rules, the availability ledger (one row per room type and night) and the stay segments of each reservation room, each recording the room type its nights consume; db adds unit exclusion and per-property gates, so a room is never assigned twice and concurrent direct bookings cannot oversell. The screens cover Today, reservations, guests, check-in and check-out with readiness checks and same-day undo, housekeeping status, and the planning grid of ui/planning, which only emits intents (create, move, split, resize) that the web app confirms after a server preview. Guest identity management includes indexed strong duplicate and shared-contact review, audited dismissal undo, two-version merges with provenance, independent current consent decisions and histories, OTA contact snapshots with ordinary-edit Apply, and owning-property reassignment (ADR 0045, guest workflow); the database’s explicit guest-reference policy inventory is guarded against catalog drift. Direct bookings support several rooms from the screens and API. Planning supports edge resizing, keyboard move mode, protected stays and inverse Undo; housekeeping reschedules its tasks from those room changes. See ADRs 0023 to 0026 and 0042, reservations and the front desk.
Rates and restrictions
Section titled “Rates and restrictions”core/reservations derives rate plans, effective nightly prices and six restrictions, quotes stays and freezes the price of each booked night; the rate-plan settings and the rate calendar edit them. Prices per person or occupancy and packages are planned. See ADR 0027 and rates.
Policies and no-shows
Section titled “Policies and no-shows”core/reservations keeps property policies and the terms frozen on each booked room: simulated cancellation fees and waivers, no-shows that release inventory, and manual guarantee and prepayment tracking. core/channex reports an OTA booking’s no-show or invalid card to its channel through Channex (booking reports). Provider authorization and collection are planned. See ADR 0032 and policies.
Room blocks and maintenance
Section titled “Room blocks and maintenance”core/blocks owns dated out-of-order blocks, which reduce availability and enqueue ARI changes, out-of-service blocks, which prevent physical assignment only, and maintenance tickets with assignees, dates, activity and fault photos behind the FileStore port of core/files, stored by db in PostgreSQL or, with object storage configured, in an S3-compatible bucket or a private Vercel Blob store through storage, with a worker job that collects orphaned objects and a command that moves the files between the two; a fault report can create a block through the same rules; Planning, Rooms and Maintenance show them. Automatic photo retention is planned. See ADR 0033 and ADR 0046.
Housekeeping
Section titled “Housekeeping”core/reservations plans the tasks a stay owes (departure cleans, cleans of a room a move vacates, stayover service) and every reservation write reschedules them in its transaction; core/housekeeping generates days on demand, assigns tasks and runs their lifecycle, which sets the room status and check-in readiness. The web board and the mobile app’s “My tasks” work on them. The app’s offline outbox is part of the mobile offline layer and is planned here. See ADR 0046 and housekeeping.
Folios and payments
Section titled “Folios and payments”core/folios keeps an append-only guest account: room charges frozen and reconciled by reservation writes in the same transaction, catalog extras, manual payments and refunds, check-out settlement at the last room, a printable non-fiscal statement and an audited re-rate. The asynchronous TaxCalculator port is where country modules compute taxes; no tax rules, fiscal documents, split accounts or online payment providers exist yet. See ADR 0029 and folios.
Night audit
Section titled “Night audit”core/night-audits runs resumable manual and scheduled property audits, with separate settings and health: blocking stay checks, acknowledged folio and OTA warnings, option expiry, configured no-shows through frozen policies, room-charge reconciliation and a one-day business-date advance. The existing worker schedules local-time execution; ordinary Vercel traffic wakes due work after the response, with a daily cron safety net. core/cash-shifts owns drawer settings, opening float, server-attributed cash movements, counted closure and immutable reports. See ADR 0043 and the night audit.
Reports
Section titled “Reports”core/reports and set-based SQL in db compute per-property performance with historical room/type/OOO capacity, daily pickup from captured reservation history, cash receipts/refunds by method and service date, and folio revenue by posting category/tax category and service date. Every report has a chart, table and CSV under the property-scoped reports.view grant. History starts at upgrade or property creation; pickup requires captured history, while older performance periods use labelled baseline inventory. See ADR 0031 and reports.
Channex connection and booking import
Section titled “Channex connection and booking import”channex-client is the typed Channex client, channex the adapter, and core/channex the use cases. A workspace connects its own Channex key, stored encrypted under ENCRYPTION_KEYS. Booking import maps properties and room types, receives verified webhook signals, keeps a durable revision feed with erasable personal content, projects bookings into the availability ledger, acknowledges them and recovers from overbooking and quarantine. Administrators import a property’s existing bookings after a preview, and reinstated bookings become active again (ADR 0048). See ADRs 0021 and 0028, and booking import.
Channex ARI publication
Section titled “Channex ARI publication”core/channex, channex and the server pumps publish absolute availability, prices and restrictions from the ledger and effective rates through a transactional outbox, after plan mapping and an explicit takeover, with numbered property leases, a booking barrier, shared pacing and health with resync. Lost sends are settled by provider request lookup, a nightly check repairs drift from Channex’s values, and alerts reach the property’s screens and the webhook catalog (ADR 0048). Partial: the certification run with Channex staff is planned. See ADR 0034 and ARI publication.
Channex Messages
Section titled “Channex Messages”core/channex, channex and server/http activate the paid Channex Messages app per property, read threads from the property-filtered live feed into a shared inbox, and send replies, templates and booking or inquiry attachments through an outbox that never sends twice. Operators confirm booking and alteration decisions, pre-approve inquiries or send offers in the inquiry currency, configure workspace retention, and erase a selected guest’s local message content. Inquiry attachment delivery is simulator-verified; live Channex certification remains pending. Upstream copies and booking revision archives are outside Messages erasure. See ADR 0044 and Messages.
API keys and outbound webhooks
Section titled “API keys and outbound webhooks”core/integrations narrows key grants to the creator’s live access, rotates keys and checks their IP allow-lists, and defines the event catalog (event-catalog.ts, the one place that lists delivered types), the events API, endpoints, replays and the delivery schedule; db writes a webhook outbox row with each catalog event in the same transaction and resolves each event’s property and reservation per kind of object; auth resolves gc_live_… bearer keys beside device tokens and judges each request by its own client address; server seals signing secrets, checks destinations and sends through the guarded transport; jobs dispatches and delivers. Settings manages keys, endpoints, replays and redeliveries, and lists events. The catalog renders each event in the version each endpoint chose; jobs runs one delivery job per workspace, and endpoints back off on their own. See ADR 0040 and API keys and webhooks.
OAuth applications and the built-in MCP server
Section titled “OAuth applications and the built-in MCP server”auth runs Better Auth’s OAuth provider behind its guard and resolves gc_oat_… tokens through the same gateway as keys, at the resource they were issued for, as the consenting member’s live grant (core/integrations/oauth-applications.ts); db keeps the grants and Better Auth’s OAuth tables; server serves the discovery documents, the consent’s server side and /api/mcp, where @guestcraft/mcp calls the API in process as its caller and confirms destructive tools once through a ledger; apps/web has the consent page and Settings → Authorized applications. See ADR 0047, OAuth applications and the MCP server.
Mobile app and device sessions
Section titled “Mobile app and device sessions”apps/mobile (Expo, MIT) reaches any instance through the SDK: public discovery at /.well-known/guestcraft, a browser sign-in that hands a revocable device session to the app, then Today, single-room housekeeping status, “My tasks” with one-tap status and Undo, task and room detail, and fault reports with photos. Partial: no store release exists; push notifications, offline changes and the other screens are planned. See ADR 0041 and discovery and device sessions.
Country packs
Section titled “Country packs”core/countries is the contract; countries/it holds Italy’s cash rounding and consumer price rules, countries/fictitious a test country, and countries/conformance the kit that checks every pack. No workflow consumes the Italian rules yet, and Italy’s compliance modules (tourist tax, guest registration, e-invoicing) are planned. See ADR 0015.
Languages and the design system
Section titled “Languages and the design system”i18n holds the English catalog and locale-aware formatting of money, dates and measures; tokens and ui provide the design system on React Aria, with light, dark and high-contrast themes and three densities. The application ships English only. See ADRs 0012, 0013 and 0018.
Organization and workspace lifecycle
Section titled “Organization and workspace lifecycle”ADR 0049 extends the guarded identity capability with instance-admin organization provisioning, workspace settings and reversible archival. The public API and SDK expose these workflows; organization Activity reads the organization audit scope, and the browser directory resolves live access across memberships. The tenancy guide states archive consequences and the intentionally absent workspace locale/measurement defaults.