Skip to content

c0dewhacker/Roomer

Repository files navigation

Roomer

Self-hosted workspace booking — desks, assets, and floor plans, fully under your control.

License: AGPL v3 Docker GitHub Actions Workflow Status


What is Roomer?

Roomer is a self-hosted platform for managing desk and asset reservations across offices, buildings, and floors. Upload floor plans, place bookable assets on a canvas, and let your team book space — without handing your occupancy data to a third-party SaaS.


Features

Floor plan editor Upload image, PDF or DXF floor plans and place assets on an interactive canvas
Desk & asset booking Book by date with live availability (available, booked, assigned, queued, restricted)
Permanent assignments Assign desks to users with primary/secondary ownership; bulk-assign via CSV
Zone management Group assets into colour-coded zones with optional conflict rules
Building & floor management Building admins and floor managers for delegated admin without full super-admin access
Queue & waitlist Users join a waitlist and are automatically promoted when a desk frees up
Recurring bookings Daily/weekly/monthly recurring booking rules that materialise the whole series up front
Availability windows Permanently-assigned desks can be opened for booking on specific date ranges
Floor subscriptions Users subscribe to a floor and are notified when matching space frees up
Analytics Utilisation and booking analytics for admins and building managers
Building leases Track building lease terms and costs (admin)
Departments Department hierarchy with user membership, mappable from OIDC/SAML/LDAP/SCIM
Bulk CSV import Import buildings, floors, zones, and assets in a single pass
Asset registry Track non-bookable inventory (laptops, monitors, etc.) alongside bookable space
Role-based access Super admin, building admin, floor manager, and user roles
Multi-provider login Local, LDAP, OIDC, and SAML — mix providers with a configurable default and URL overrides
SCIM 2.0 provisioning Automated user and group sync from Okta, Azure AD / Entra ID, OneLogin, etc.
Webhooks Outbound webhook endpoints with HMAC-SHA256 signed payloads, delivery log with retry, and 18 event types across booking, queue, asset, and user domains
Email notifications Booking confirmations and queue promotions via SMTP

Roomer-demo


Quick Start (Docker)

The fastest way to run Roomer is with the pre-built Docker images.

1. Create a .env file in the project root:

ROOMER_SESSION_SECRET=<run: openssl rand -hex 32>
ROOMER_ENCRYPTION_KEY=<run: openssl rand -hex 32>
APP_ORIGIN=http://localhost
ROOMER_COOKIE_SECURE=false
WEB_PORT=80
ROOMER_EMAIL_FROM=noreply@roomer.local

# Admin account credentials (see "Seeding" below)
ROOMER_SEED_ADMIN_EMAIL=admin@roomer.local
ROOMER_SEED_ADMIN_PASSWORD=changeme

2. Start everything:

docker compose up -d

The web app will be at http://localhost (or WEB_PORT if changed). The API container automatically runs prisma migrate deploy on startup, then seeds the admin account on first run.

Find the admin password: the seed step prints it to the API logs:

docker compose logs api | grep "seed"

If SEED_ADMIN_PASSWORD is not set, a random password is generated and logged there. Set it in your .env to use a known password.

3. (Optional) Seed demo buildings and desks:

Add SEED_DEMO_DATA=true to your .env (before first start, or re-run the seed manually):

docker compose exec api npx prisma db seed

This adds a sample building ("Acme HQ") with a floor, two zones, and six desks, plus a regular test user (user@roomer.local). Change all passwords after first login.

Docker Compose environment variables

All API variables use the ROOMER_ prefix in the root .env. Unprefixed names are still accepted by the API itself (for local dev), but Docker Compose and Kubernetes always inject the ROOMER_ form.

Variable Default Description
ROOMER_SESSION_SECRET Required. 32+ character random string. Generate with openssl rand -hex 32.
ROOMER_ENCRYPTION_KEY Required. 64 hex-char AES-256 key for encrypting auth provider secrets at rest. Generate with openssl rand -hex 32.
APP_ORIGIN http://localhost Public URL used for CORS_ORIGIN and APP_URL on the API container.
ROOMER_COOKIE_SECURE true Set to false only for plain HTTP testing.
WEB_PORT 8999 Host port for the web container.
ROOMER_EMAIL_FROM noreply@roomer.local Sender address for system emails.
ROOMER_SEED_ADMIN_EMAIL admin@roomer.local Email for the super-admin account created on first seed.
ROOMER_SEED_ADMIN_PASSWORD (random) Password for the super-admin account. If unset, a random password is generated and printed to the API container logs.
ROOMER_SEED_DEMO_DATA false Set to true to seed demo buildings, floors, desks and a test user on first start.
ROOMER_SEED_USER_PASSWORD (random) Password for the demo test user (user@roomer.local). Only used when ROOMER_SEED_DEMO_DATA=true.

Build from source

docker compose -f docker-compose.build.yaml up --build

Docker images

Image Description
c0dewhacker/roomer-api Multi-stage Node 24 Alpine. Runs migrations then starts Fastify.
c0dewhacker/roomer-web Multi-stage Vite build served by nginx 1.27. Proxies /api to the API.

Both images are built from the monorepo root so they can access packages/shared.


Stack

Layer Technology
API Fastify + TypeScript
Database PostgreSQL 18 + Prisma ORM
Frontend React 18 + Vite + Tailwind CSS + shadcn/ui
Canvas Konva (react-konva)
Job queue pg-boss (PostgreSQL-backed)
Monorepo pnpm workspaces + Turborepo

Development Setup

Prerequisites

  • Node.js v24+ (required by Prisma 7)
  • pnpm v10+ — npm install -g pnpm@10
  • Docker and Docker Compose

1. Clone and install

git clone https://github.com/c0dewhacker/Roomer.git roomer
cd roomer
pnpm install

2. Start infrastructure

docker compose up -d

This starts PostgreSQL and Mailpit (local SMTP catcher for dev email):

Service URL Notes
PostgreSQL localhost:5435 User/pass/db: roomer
Mailpit SMTP localhost:1025 Dev email relay
Mailpit Web http://localhost:8025 View outbound email

3. Configure the API

Create apps/api/.env:

# Required
DATABASE_URL=postgresql://roomer:roomer@localhost:5435/roomer
SESSION_SECRET=<openssl rand -hex 32>
# AES-256-GCM key for encrypting auth provider secrets (OIDC/LDAP/SAML)
ROOMER_ENCRYPTION_KEY=<openssl rand -hex 32>

# Defaults — override as needed
NODE_ENV=development
PORT=3001
CORS_ORIGIN=http://localhost:5173

# Email (matches Mailpit defaults above)
SMTP_HOST=localhost
SMTP_PORT=1025
SMTP_SECURE=false
EMAIL_FROM=noreply@roomer.local
APP_URL=http://localhost:5173

# Seed credentials — set these before running db:seed so you know the password.
# If SEED_ADMIN_PASSWORD is omitted, a random password is generated and printed to stdout.
SEED_ADMIN_EMAIL=admin@roomer.local
SEED_ADMIN_PASSWORD=admin123
SEED_DEMO_DATA=true

All variables also accept the ROOMER_ prefix (e.g. ROOMER_DATABASE_URL). ROOMER_<NAME> takes precedence when both are set — Docker Compose and Kubernetes always use the prefixed form. For local dev the unprefixed names are fine.

4. Migrate and seed

pnpm --filter api db:migrate   # apply migrations
pnpm --filter api db:seed      # seed demo data

With the .env values above, the seed creates:

Resource Value
Admin login admin@roomer.local / admin123 (from your .env)
Organisation Acme Corp (renamed when SEED_DEMO_DATA=true)
Building Acme HQ with Ground Floor — 6 sample desks across two zones
Test user user@roomer.local (password from SEED_USER_PASSWORD, or logged if unset)

Without SEED_DEMO_DATA=true, only the admin account and a blank organisation ("My Organisation") are created.

5. Start dev servers

pnpm dev                      # both apps via Turborepo
# or individually:
pnpm --filter api dev          # API  → http://localhost:3001
pnpm --filter web dev          # Web  → http://localhost:5173

Open http://localhost:5173 and sign in with admin@roomer.local / admin123.


Production Deployment

The Docker images are production-ready as-is. Key checklist:

  • Set COOKIE_SECURE=true and serve over HTTPS
  • Set APP_ORIGIN to your public domain (e.g. https://roomer.example.com)
  • Set API_PUBLIC_URL to your public API URL if different from APP_ORIGIN (used for SCIM endpoint links)
  • Use a managed PostgreSQL instance by overriding DATABASE_URL in the API environment
  • Mount a persistent volume at /app/uploads for floor plan storage (already in docker-compose.yml)
  • For horizontal API scaling, point FILE_STORAGE_PATH at shared network storage rather than a local volume

Kubernetes (Helm)

A Helm chart is included at charts/roomer/. Fetch sub-chart dependencies, then install:

helm dependency update ./charts/roomer
helm install roomer ./charts/roomer \
  --namespace roomer --create-namespace \
  --set ingress.host=roomer.example.com \
  --set secrets.sessionSecret=$(openssl rand -hex 32) \
  --set secrets.encryptionKey=$(openssl rand -hex 32) \
  --set secrets.databaseUrl="postgresql://user:pass@host:5432/roomer"

TLS with cert-manager (Let's Encrypt):

helm install roomer ./charts/roomer \
  --namespace roomer --create-namespace \
  --set ingress.host=roomer.example.com \
  --set ingress.tls.enabled=true \
  --set ingress.tls.certManager.enabled=true \
  --set ingress.tls.certManager.issuerName=letsencrypt-prod \
  --set secrets.sessionSecret=$(openssl rand -hex 32) \
  --set secrets.encryptionKey=$(openssl rand -hex 32) \
  --set secrets.databaseUrl="postgresql://user:pass@host:5432/roomer"

See the Kubernetes Deployment wiki page for the full values reference and upgrade instructions.


API Reference

The REST API runs on port 3001 by default.

  • Swagger UI: http://localhost:3001/docs (enabled in development; set SWAGGER_ENABLED=true to enable in production)
  • All routes are prefixed /api/v1
Prefix Description
/api/v1/auth Login, logout, session, enterprise SSO (OIDC/SAML/LDAP)
/api/v1/buildings Buildings CRUD, managers, access groups
/api/v1/floors Floors, zones, floor plan upload, availability
/api/v1/assets Asset registry, bookable desks, user assignments, bulk assignment CSV
/api/v1/bookings Booking lifecycle
/api/v1/queue Waitlist management
/api/v1/import/bulk Global CSV bulk import
/api/v1/users User management
/api/v1/groups Group management
/api/v1/settings Organisation settings, auth provider config, SCIM provisioning
/api/v1/webhooks Webhook endpoint management, delivery log
/scim/v2 SCIM 2.0 provisioning (separate prefix, bearer token auth)
/health/live Liveness probe — always 200 while the process is running
/health/ready Readiness probe — 200 when the DB is reachable, 503 otherwise
/metrics Prometheus metrics (enabled via ROOMER_METRICS_ENABLED=true)

Configuration Reference

All API environment variables. Every variable accepts a ROOMER_ prefix (e.g. ROOMER_DATABASE_URL); the prefixed form takes precedence when both are set. Docker Compose and Kubernetes always inject the ROOMER_ form.

Variable Default Description
DATABASE_URL PostgreSQL connection string (required)
SESSION_SECRET Cookie signing secret, min 32 chars (required)
ROOMER_ENCRYPTION_KEY 64-char hex AES-256 key for encrypting auth provider secrets at rest (required). Generate with openssl rand -hex 32.
PORT 3001 API listen port
HOST 0.0.0.0 API bind address
CORS_ORIGIN http://localhost:5173 Allowed frontend origin
COOKIE_SECURE false in dev Require HTTPS for session cookies
TRUST_PROXY false in dev Trust X-Forwarded-For headers
ALLOW_BEARER_AUTH true in dev Accept Authorization: Bearer tokens
SWAGGER_ENABLED true in dev Expose Swagger UI at /docs
ROOMER_METRICS_ENABLED false Set to true to expose a Prometheus /metrics endpoint. Unauthenticated unless METRICS_TOKEN is set — protect at the network/ingress level.
METRICS_TOKEN Optional bearer token for /metrics. When set, scrapers must send Authorization: Bearer <token>.
WEBHOOK_ALLOW_PRIVATE false Allow webhook delivery to private/RFC1918 addresses (internal integrations). Loopback and link-local (incl. cloud metadata) remain blocked regardless.
FILE_STORAGE_PATH ./uploads Directory for floor plan images
MAX_FILE_SIZE_MB 20 Maximum upload size
API_PUBLIC_URL http://localhost:3001 Public URL of the API — shown in the SCIM settings panel as the endpoint base URL
SMTP_HOST localhost Outbound email relay host
SMTP_PORT 1025 Outbound email relay port
SMTP_SECURE false Use TLS for SMTP
SMTP_USER SMTP username (if required)
SMTP_PASS SMTP password (if required)
EMAIL_FROM noreply@roomer.local Sender address for system emails
APP_URL http://localhost:5173 Public URL used in email links

Roles

Role Scope Permissions
SUPER_ADMIN Global Full access to all resources and settings
BUILDING_ADMIN Per building Superset of floor manager for every floor in the building
FLOOR_MANAGER Per floor Manage zones, assets, and bookings on assigned floors
USER Global Book available assets, manage own bookings

Available Scripts

Run from the monorepo root:

Command Description
pnpm dev Start all apps in development mode
pnpm build Build all apps for production
pnpm lint Lint all packages
pnpm --filter api db:migrate Run pending Prisma migrations
pnpm --filter api db:seed Seed demo data
pnpm --filter api db:studio Open Prisma Studio (database browser)

Project Structure

roomer/
├── apps/
│   ├── api/              # Fastify REST API
│   │   ├── prisma/       # Schema, migrations, seed
│   │   └── src/
│   │       ├── routes/
│   │       ├── middleware/
│   │       └── lib/
│   └── web/              # React SPA
│       └── src/
│           ├── components/
│           ├── pages/
│           ├── hooks/
│           └── stores/
├── packages/
│   └── shared/           # Shared types and Zod schemas
├── docker-compose.yml        # Pull pre-built images
├── docker-compose.build.yaml # Build from source
└── turbo.json

License

Roomer is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).

In short: you can use, modify, self-host, and distribute Roomer freely, including commercially. If you modify Roomer and make it available to others over a network (e.g. as a hosted service), you must release your modified source under the same license.

See the LICENSE file for the full terms.

About

Roomer is a self-hosted platform for managing desk and asset reservations across offices, buildings, and floors. Upload floor plans, place bookable assets on a canvas, and let your team book space — without handing your occupancy data to a third-party SaaS.

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages