# Technical documentation — everspace (Everyspace)

Evidence date: 2026-10-15.

Source: `git@bitbucket.org:overdrive/everspace.git`, `master` `c784099` (2025-11-05, "Request changes"). Author of all three commits on this remote: Jaco Brits. First commit `18fcb3f` on 2022-08-30. The tree is a full Meteor application; the git history is not a full development history.

Public brand in UI and `pm2.json`: Everyspace, `ROOT_URL` `https://everyspace.co.za/`. npm name: `filmspace-meteor_react`.

This document does not repeat credentials, tokens, passwords, or connection strings. It names the files that contain them.

## Architecture

Single Meteor 2.4 application.

- Client entry: `client/main.js` imports API client barrels and `/dist/assets/css/app.css`.
- Server entry: `server/main.js` imports API server barrels. `imports/api/ServerRender/server` is commented out.
- UI: React 16.12, `react-mounter`, `meteor/react-meteor-data`.
- Router: `ostrio:flow-router-extra`, titles and meta via `ostrio:flow-router-meta` and `ostrio:flow-router-title`. Router waits until `Roles.subscription.ready()` (`imports/routes/router.js`).
- Data: MongoDB through Meteor collections. Schemas use `simpl-schema` and `aldeed:collection2` where attached.
- Files: `ostrio:files` for images. Sharp builds watermarked versions.
- Background work: Mongo collection `job_queue`, `Meteor.setTimeout`, `node-cron`. Not a separate worker service.
- PDFs: Puppeteer. The production image installs Chromium and sets `PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser`.

CSS is not only Meteor. `src/` is a Foundation Sites / Panini / Gulp pipeline (`gulpfile.babel.js`, `npm run build` → `gulp build --production`). The Meteor client imports the built file `dist/assets/css/app.css`. README says Gulp was run on Node 10.16.0 after `npm install` on Node 12.13.0. The Docker image does not use those older Node versions; it uses Meteor 2.4's base image and Node 14.18.1.

## Repository layout

| Path | Role |
|---|---|
| `imports/api` | Domain modules: ActiveCampaign, Activities, Admin, App, Bookings, Counter, Intercom, JobQueue, Listings, Messaging, PeachPayments, Reviews, ServerRender, Users |
| `imports/routes` | FlowRouter route modules |
| `imports/ui` | React layouts, pages, components |
| `imports/libs` | Shared libraries |
| `client`, `server` | Meteor entrypoints |
| `private/email-templates` | Handlebars email HTML |
| `public` | Static images, fonts |
| `src` | Gulp/Foundation source |
| `packages/meteor-accounts-merge` | Local Meteor package. Also `mikael:accounts-merge` in `.meteor/packages` |
| `db/scripts` | Mongo shell scripts: `pricing-migrate.js`, `pricing-minMax.js`, `random-activities-for-testing.js` |
| `chart` | Helm chart `auto-deploy-meteor` 0.0.1, described as Meteor deploy based on GitLab auto-deploy |
| `tests/main.js` | Mocha smoke test |
| `Dockerfile` | Multi-stage image |
| `.gitlab-ci.yml` | Build, staging, production. `.gitlab-ci.old.yml` is also present |
| `pm2.json` | PM2 app definition |
| `deploy.sh`, `deploy.bat`, `db.bat` | Shell helpers |
| `dev.settings.json`, `settings.json` | Meteor settings files |
| `config.yml` | Present at repo root. Not read as an application config of record; Gulp/Panini-style config is the likely consumer. **Requires confirmation** if a human still uses it |

`__CODEOWNERS` is present. No `CHANGELOG.md` and no `docs/` directory are in this commit. A September 2026 discovery listing named `CHANGELOG.md` and `docs`; they are not in `master` at `c784099`.

## Frameworks and languages

| Item | Version or package | Evidence |
|---|---|---|
| Meteor | 2.4 | `.meteor/release` |
| Node in production image | 14.18.1-alpine | `Dockerfile` |
| React | ^16.12.0 | `package.json` |
| TypeScript | Meteor `typescript@4.3.5`. Some newer modules are `.ts` (activities, locations, space tags) | `.meteor/packages` |
| jQuery | ^3.6.0 | `package.json` |
| Foundation Sites | ^6.6.3 in npm. README says clone 6.5.3 into `/packages` | Both statements exist. **Requires confirmation** which copy the Gulp build uses |
| Roles | `alanning:roles` | `.meteor/packages` |
| Accounts | `accounts-password@2.1.0`, `accounts-google@1.4.0`, `accounts-facebook@1.3.3` | `.meteor/packages` |

`insecure` and `autopublish` are commented out in `.meteor/packages`.

## Mongo collections

| Collection | Module |
|---|---|
| `users` (Meteor) | Accounts. Profile, mobiles, cards, settings, banned, Intercom id |
| `newsletter` | Users |
| `listings` | Listings |
| `categories` | Listings |
| `forms_for_categories` | Listings |
| `locations` | Listings. Schema attached |
| `spaces_tags` | Listings |
| `spaces_tags_images_links` | Listings |
| `activities` | Activities |
| `filters.categories`, `filters.features`, `filters.styles` | Client search filter collections |
| `bookings` | Bookings |
| `pre_bookings` | Bookings |
| `message_threads` | Messaging |
| `listing_reviews`, `user_reviews` | Reviews |
| `change_requests` | Admin |
| `job_queue` | JobQueue |
| `counter` | Counter (booking reference sequence) |

Image files use `ostrio:files` (`ImagesDB`). Booking PDFs use a documents collection under `imports/api/Bookings`.

## Environment configuration

`npm start` runs:

`meteor run --settings dev.settings.json --exclude-archs web.browser.legacy,web.cordova`

`start-dev-db` sets `MONGO_URL` in the script. The URL in `package.json` includes credentials. Not copied here.

Variables the code and deploy files expect:

| Name | Used for |
|---|---|
| `MONGO_URL` | Meteor database |
| `ROOT_URL` | Public URL |
| `PORT` | HTTP port. Examples use 3000 |
| `BIND_IP` | Docker examples set `0.0.0.0`. `pm2.json` sets `127.0.0.1` |
| `MAIL_URL` | SMTP |
| `DO_ACCESS_KEY`, `DO_SECRET` | Object storage in production (`process.env`). Development reads `Meteor.settings.DO_SPACES.ACCESS_KEY` and `.SECRET` |
| `ACTIVE_CAMPAIGN_URL`, `ACTIVE_CAMPAIGN_KEY`, `ACTIVE_CAMPAIGN_TRACK_KEY`, `ACTIVE_CAMPAIGN_TRACK_ACCOUNT_ID` | ActiveCampaign |

Docker examples also pass those variables on the `docker run` command line inside `README.md`.

## Object storage

`imports/api/Listings/server/init/digital-ocean-spaces-client.js` constructs `AWS.S3` with `region: 'af-south-1'`. The endpoint `fra1.digitaloceanspaces.com` is commented out. Variable names say DigitalOcean. Key prefix style in the committed examples is an AWS access key id. **Requires confirmation** whether objects are in AWS S3 Cape Town or in DigitalOcean Spaces.

Watermarks: `createWatermarkedVersionForImage.js`, methods `listings.ensureWatermarkedImages` and `listings.getWatermarkedImageUrl`. Admin method `admin.deleteAllWatermarkedImages`.

## Payments

`PeachPaymentsService` (server):

- Base URL in use: `https://oppwa.com`. Sandbox `https://test.oppwa.com` is commented out.
- Bearer token and entity id are constants in the file.
- Payment types: `PA` pre-authorisation, `DB` debit, `CD` credit, `CP` capture, `RV` reversal, `RF` refund.
- Checkout: `POST /v1/checkouts`. Status: `GET` on the returned resource path. Follow-up: `POST /v1/payments/{id}`.

Booking methods are the only callers that need to be treated as the product flow (`bookings.preparePeachPaymentCheckout`, `bookings.getPeachPaymentsPaymentStatus`, accept, decline, cancel). See the PRD workflows.

## Other integrations

| Integration | How it is configured | Notes |
|---|---|---|
| Google OAuth | `ServiceConfiguration` upsert in `authentication_services.js` | Client id and secret hardcoded |
| Facebook OAuth | Same file | App id and secret hardcoded |
| reCAPTCHA | `recaptchaVerify` posts to Google `siteverify` | Secret hardcoded |
| Twilio | `sms.js` | Account SID, token, and sending number hardcoded. `sendSMS` logs failure and does not throw a Meteor error |
| Intercom | `intercom-client` and browser `window.Intercom` | Events are named in the methods that call `EveryspaceIntercomClient.trackEvent` |
| ActiveCampaign | npm `activecampaign`, env vars | Contact field updates around listings and bookings |
| GTM | Hardcoded `UA-158596565-1` | Sent on path change |
| Facebook pixel | `fbq("track", "PageView")` | Assumes `fbq` exists on the page |
| Email | `meteor/email`, Handlebars templates under `private/email-templates` | Contact-us and password-change templates are compiled at startup of the users methods file |

## Auth model

- Meteor accounts. Password login blocked until the profile email is verified (see business rules).
- Roles: global `admin`, global `host`, listing-scoped `editor`.
- Client acting mode `userActingAs` is not a role.
- OAuth users: `onCreateUser` copies name and email from Google or Facebook into `profile` and clears `user.emails` before `afterUserCreated` calls `Accounts.addEmail` and `Accounts.sendVerificationEmail`.
- Every new user inserts a `newsletter` document and sets `banned: false`.
- `Accounts.onLogin` creates or updates an Intercom contact.
- Remember-me is a `localStorage` key `Meteor.rememberMe`, not a separate server session type in the code that was read.

Bootstrap admin creation runs from `imports/api/Admin/server/init/create_admin.js` for three email addresses. See business rules. Do not copy the password into new systems.

## Job queue

`JobQueueService` stores `{ method, description, args, date, status }`.

On startup (`job-queue.js`):

1. All jobs set to pending, `serverId` null.
2. Jobs with `date` before now are removed.
3. Pending jobs from the start of today through the start of the day two days ahead are observed and armed with `Meteor.setTimeout`.
4. Removing a scheduled job clears its timeout.
5. Cron `0 8 * * *` calls `enqueueJobs` again.

Success removes the job. Failure logs and leaves it. There is no retry loop.

Scheduled product jobs:

| Method | When |
|---|---|
| `bookings.onHostApprovalTimeExpires` | 3 days after successful pre-authorisation |
| `bookings.24hourBookingRequestReminder` | 24 hours after that |
| `bookings.onBookingEndTime` | `endDate` of the booking |
| `bookings.48hourReminder` | 48 hours before first timeslot, only if that start is more than 96 hours after accept |

Review email methods schedule at now plus 48 hours through the same style of delay (see `imports/api/Reviews/server/methods/emails.js`).

Helpers `bookings.setExpiresIn` and `bookings.setEndDateIn` reschedule the approval or end job. They are Meteor methods with no role check in the body that was read. **Requires confirmation.**

## Deployment

### Docker

`Dockerfile`:

1. `geoffreybooth/meteor-base:2.4`, `meteor npm run build` (the RUN line ends with `; exit 0`, so a Gulp failure does not fail the image build).
2. `build-meteor-bundle.sh`.
3. `node:14.18.1-alpine`, rebuild native modules, remove Sharp vendor binaries so Sharp rebuilds, `npm install request` in the server program.
4. Final stage installs Chromium and related packages, copies the bundle, writes `/app.tar.gz` in the image, entrypoint `/docker/entrypoint.sh`, command `node main.js`.

The comment in the Dockerfile says the Node version is the one expected for Meteor 1.11.1. The image tag is Node 14.18.1 and `.meteor/release` is 2.4. **Requires confirmation** that 14.18.1 is the Node version Meteor 2.4 expects. The comment and the release disagree.

### GitLab CI

`.gitlab-ci.yml` stages: `build`, `staging`, `production`.

- Build uses `docker:20.10.12`, BuildKit, pushes `${CI_REGISTRY_IMAGE}` tags, copies `app.tar.gz` out of a container named `app`. Runner tag `docker-cached`.
- Staging: GitLab `auto-deploy-image:v2.18.1`, namespace `${CI_PROJECT_PATH_SLUG}-staging`, environment URL `http://filmspace-staging.$KUBE_INGRESS_BASE_DOMAIN`, runs when the branch is the default branch.
- Production: same image, namespace `${CI_PROJECT_PATH_SLUG}-production`, URL `http://filmspace.$KUBE_INGRESS_BASE_DOMAIN`, `needs: staging`, **manual**, default branch only.

The chart in `chart/` is what `auto-deploy download_chart` may replace or complement. **Requires confirmation** which chart the pipeline actually deploys. The job script calls `auto-deploy download_chart` rather than `helm upgrade` on `chart/` directly.

### Other deploy notes (README and pm2)

README still documents:

- `meteor build` and `scp` to `codeswop@filmspace.codeswop.com` and `filmspace.co.za`, then `pm2 restart`.
- `docker build -t everyspace:latest` and `docker run` examples, including host `156.38.132.154` and Mongo on that host, dated 11 November 2025 in the README.
- An Ubuntu plus nginx plus Certbot write-up aimed at a single VM.

`pm2.json` names the process `Everyspace`, script `/home/ubuntu/everyspace/bundle/main.js`, `ROOT_URL` `https://everyspace.co.za/`, `BIND_IP` `127.0.0.1`, `PORT` 3000.

**Requires confirmation** which of these is production.

## Security findings (evidence, not a pentest)

These are committed or hardcoded. Treat them as exposed. Rotate them. Do not paste the values into tickets or docs.

| Location | What is committed |
|---|---|
| `README.md` | Example `docker run` lines with Mongo URL, ActiveCampaign keys, object-storage keys, SMTP URL |
| `pm2.json` | Mongo URL, SMTP URL, object-storage keys, ActiveCampaign keys |
| `package.json` script `start-dev-db` | Mongo URL |
| `imports/api/PeachPayments/server/services/PeachPaymentsService.js` | Gateway bearer token and entity id (live constants; sandbox constants also present in comments) |
| `imports/api/Users/server/init/sms.js` | Twilio account SID, token, sending number |
| `imports/api/Users/server/init/authentication_services.js` | Google client secret and Facebook app secret |
| `imports/api/Users/server/methods/users.js` method `recaptchaVerify` | reCAPTCHA secret |
| `imports/api/Admin/server/init/create_admin.js` | Bootstrap password and admin emails |
| `server/main.js` | A commented `MAIL_URL` and a commented `Accounts.sendResetPasswordEmail` call with a user id |

Further code issues to review, already described in the PRD:

- `toggleUserIsHost` has no authorisation check in the method body.
- `AdminListings` publication references an undefined `userId`, so the admin role check does not work as written.
- `bookings.setExpiresIn` / `bookings.setEndDateIn` have no role check in the body that was read.
- `/testing`, `/dev-test`, receipt routes, and invoice routes do not declare a login `triggersEnter` in their route files.
- `Accounts.onLogin` logs `Meteor.user()` to the server console.

## Development and test process

| Command | Effect |
|---|---|
| `npm start` | Meteor with `dev.settings.json`, excluding legacy browser and Cordova archs |
| `npm run start-dev-db` | Same, with `MONGO_URL` pointing at a remote dev database |
| `npm test` | `meteor test --once --driver-package meteortesting:mocha` |
| `npm run test-app` | Full-app mocha with `TEST_WATCH=1` |
| `npm run gulp` / `npm run build` | Asset pipeline |

`tests/main.js` asserts the package name is `filmspace-meteor_react` and that the client is not the server. There are no booking, pricing, or permission tests in that file.

Mongo restore note in README: `mongorestore -h 127.0.0.1 --port 3001 --db meteor ..\dump\filmspace`. Local Meteor Mongo is the usual port 3001.

## Monitoring

No application APM, health endpoint, or log shipper is configured in the repository. Operational signals that do exist:

- `console.log` / `console.error` in payment methods, job runner, SMS, and login.
- Intercom event stream.
- ActiveCampaign contact fields.
- Google Tag Manager and Facebook pixel in the browser.
- PM2 and Docker logs, depending on which deploy is live.
- GitLab environments `staging` and `production`.

**Unknown / Requires Confirmation:** where production logs and uptime are watched.

## Discrepancies to confirm

1. Everyspace vs Filmspace naming across UI, package, CI hostnames, and README hosts.
2. AWS S3 `af-south-1` vs DigitalOcean Spaces.
3. Dockerfile comment (Meteor 1.11.1 / Node) vs `.meteor/release` 2.4 vs image Node 14.18.1.
4. `meteor npm run build` in Docker is the Gulp script, and the RUN allows failure (`exit 0`).
5. GitLab production URL still uses the `filmspace` hostname prefix.
6. Intercom event names on owner listing-status changes look swapped (`host_requested_publish` on inactive, `host_deactivated_listing` on active). See `listing.status.set`.
7. Three-commit history cannot explain the 2021 README milestones.
