# Technical documentation — tom

Evidence date: 2026-10-01. Source: `git@bitbucket.org:overdrive/tom.git`, `master` at `0ceed11` (`0ceed11f8d2ae89774333338be57c59cb86c1260`).

Secret values are described only by where they sit. They are not copied into this file.

## Stack

| Layer | What is in the repo | Provenance |
|---|---|---|
| Language | PHP (WordPress). No Composer project at the root. No `package.json` | **Implemented** |
| CMS | WordPress 4.9.21 (`$wp_version` in `wp-includes/version.php`). Database schema revision `$wp_db_version` 38590 | **Implemented** |
| Theme | OMTOM 1.0, text domain `omtom`, GPL v2 or later. Author Hero Digital. Requires WordPress 4.9.8 per `README.txt`. Tested up to 4.9.8-trunk per the same file | **Documented** in the theme |
| Front-end libraries in the theme | Owl Carousel, Flatpickr, Moment, Pikaday, theme SCSS (`assets/scss/tom.scss`, `admin.scss`) compiled to `assets/css/tom.css` and `admin.css` | **Implemented** |
| Database | MySQL/MariaDB via WordPress. Name `twooceansmarathon`. Charset `utf8`. Collate empty. Prefix `wp_` | **Implemented** in `wp-config.php` |
| Cache plugin | W3 Total Cache 0.9.7.2 vendored. `WP_CACHE` is `false` | **Implemented** |
| Tests | None found | **Implemented** absence |
| CI | No `bitbucket-pipelines.yml` | **Implemented** absence |

PHP version on a server is **Unknown**. `phpinfo.php` at the web root calls `phpinfo()` and would reveal it if that file is reachable.

## Repository layout

```text
tom/
  index.php, wp-load.php, wp-settings.php, wp-login.php, ...   WordPress root
  wp-config.php                                                 tracked configuration
  wp-config-sample.php                                          stock sample
  phpinfo.php                                                   phpinfo()
  wordfence-waf.php                                             Wordfence auto_prepend stub
  license.txt, readme.html                                      stock WordPress
  omtom-favicon.ico, two-oceans-favicon.ico, two-oceans-favicon.png
  .gitignore                                                    stock Bitbucket template
  wp-admin/                                                     WordPress admin
  wp-includes/                                                  WordPress core
  wp-content/
    themes/omtom/                                               the only theme
    plugins/                                                    nine plugins (see below)
```

`wp-content/mu-plugins/` is absent. `wp-content/languages/` was not a populated locale pack in the inspection (no `$wp_local_package` in `version.php`).

## Application structure (theme)

`wp-content/themes/omtom/`

| Path | Role |
|---|---|
| `functions.php` | Setup, asset enqueue, post types, taxonomies, cookie notice, Instagram fetch |
| `classes/post-types/post-type.php` | Generic registrar. Labels use text domain `peaceparks` |
| `classes/taxonomies/taxonomy.php` | Generic taxonomy registrar |
| `classes/meta-boxes/*.meta.php` | Editor fields |
| `classes/forms/forms.php` | Newsletter and contact AJAX |
| `classes/reporting/reporting.php` | Newsletter CSV AJAX |
| `classes/theme-options/` | Settings pages. Menu registration is `menus.php` |
| `classes/shortcodes/shortcodes.php` | `youtube`, `button`, `pdfLink`, `map` |
| `classes/tinymce/tinyMCE.buttons.php` | Editor buttons for those shortcodes |
| `classes/columns/custom-columns.php` | Event column on selected list tables |
| `classes/sidebars/sidebars.php` | Sidebar helper class. No instance is constructed from `functions.php` |
| `assets/js/functions.js` | Public UI, cookie banner, newsletter popup, Facebook share |
| `assets/js/maps.js` | Google Maps and KML |
| `assets/js/admin.js` | Admin UI |
| `assets/kml/`, `assets/objects/` | Route geometry |
| `assets/scss/` | Source styles. Browsers are pointed at CloudFront copies of the compiled CSS |
| `reporting/index.php` | Directory present. The active reporter writes CSV files to the uploads directory, not this folder |
| `partials/` | Blog, medical, and news loops; countdown; emergency notification; social menu |

Public theme and admin theme URLs are hardcoded to:

`https://d19jcneif5xmtf.cloudfront.net/wp-content/themes/omtom`

Enqueue calls use a protocol-relative form (`//d19jcneif5xmtf.cloudfront.net/...`). Provenance: **Implemented** in `omtom_scripts` and `omtom_admin_scripts`.

## Plugins

| Directory | Header name | Version |
|---|---|---|
| `amazon-s3-and-cloudfront` | WP Offload Media Lite | 2.0.1 |
| `crazyegg-heatmap-tracking` | Crazy Egg | 2.1 |
| `disable-xml-rpc-pingback` | Disable XML-RPC Pingback | 1.1 |
| `ssl-insecure-content-fixer` | SSL Insecure Content Fixer | 2.7.2 |
| `tablepress` | TablePress | 1.9.1 |
| `w3-total-cache` | W3 Total Cache | 0.9.7.2 |
| `wordfence` | Wordfence Security | 7.2.1 |
| `wordpress-seo` | Yoast SEO | 9.6 |
| `wp-seopress` | SEOPress | 3.3.8.1 |

Activation is stored in the database. **Unknown** which of these are active.

## Data

WordPress core tables use prefix `wp_` (posts, postmeta, options, users, and the rest of a standard install).

Custom table referenced by theme code:

| Table | Columns used | Writers | Readers |
|---|---|---|---|
| `wp_omtom_subscriptions` | `name`, `email`, `created` | `forms.php` insert (`name`, `email` only) | `reporting.php` select |

`created` is read and filtered. The insert does not set it. A database default or trigger would have to supply it. That default is **Unknown** because no `CREATE TABLE` is in git.

Theme options (WordPress `wp_options`) include: `api-facebook`, `api-twitter`, `api-instagram`, `api-google-maps`, `countdown-settings`, `social-settings`, `contact-details-settings`, `contact-race-settings`, `contact-non-race-settings`, `contact-mq-settings`, `contact-exhibition-settings`, `contact-postal-settings`, `contact-physical-settings`, `advert-settings`, `ps-1-settings`, `ps-2-settings`, `ps-3-settings`, `homepage-notification-settings`. Other option names are registered beside these in the theme-options classes.

Post meta keys used on the home page and forms include `tickets-available`, `no-tickets`, `sub-heading`, `event-date`, `color-1`, `dark-icon`, `video-url`, `content-image`, `button-label`, `button-url`, `button-target`, `information`, `enable_application_form`, `page-buttons`.

## Environment configuration

`wp-config.php` is tracked. `.gitignore` does not exclude it.

Active defines (values of secrets omitted):

| Setting | Active value in git |
|---|---|
| `DB_NAME` | `twooceansmarathon` |
| `DB_USER` | `root` |
| `DB_PASSWORD` | empty string |
| `DB_HOST` | `localhost` |
| `DB_CHARSET` | `utf8` |
| `DB_COLLATE` | empty |
| `WP_CACHE` | `false` |
| `$table_prefix` | `wp_` |
| `$_SERVER['HTTPS']` | forced `'on'` |
| `FS_METHOD` | `direct` |
| `AS3CF_SETTINGS` | serialized array, `provider` = `aws`, plus access key id and secret access key |
| `AS3CF_AWS_USE_EC2_IAM_ROLE` | commented out, so inactive |
| Auth keys and salts | eight WordPress salts plus `WP_CACHE_KEY_SALT`, all set to unique strings in git |

Commented, not executed:

| Setting | Commented value |
|---|---|
| `DB_NAME` | `twooceansmarathon` |
| `DB_USER` | `admin` |
| `DB_HOST` | `twooceans-rds.cn93a1lgmc5x.eu-west-1.rds.amazonaws.com` |
| `DB_PASSWORD` | a non-empty password is present in the comment |

`wordfence-waf.php` loads Wordfence only if this file exists:

`/var/www/html/twooceansmarathon/wp-content/plugins/wordfence/waf/bootstrap.php`

It sets `WFWAF_LOG_PATH` to `/var/www/html/twooceansmarathon/wp-content/wflogs/`. The file's own comment says to check PHP `auto_prepend_file` before removing it. Whether `auto_prepend_file` points here is **Unknown**.

## Integrations and endpoints

| Endpoint | Where |
|---|---|
| `admin-ajax.php?action=handle_subscriptions` | `forms.php` |
| `admin-ajax.php?action=handle_contacts` | `forms.php` |
| `admin-ajax.php?action=newsletter_reporting` | `reporting.php` |
| `https://api.touchbasepro.com/email/subscribers/{list id}` | POST JSON from `forms.php`. List id and bearer token are hardcoded. Omitted here |
| `https://api.instagram.com/v1/users/self/media/recent` | GET from `pull_instagram_feed()` |
| `//platform.twitter.com/widgets.js` | enqueued on the public site |
| Facebook JS SDK `v2.7` | `assets/js/functions.js` |
| Google Maps JS | `assets/js/maps.js`, key from option `api-google-maps`, also a key left in a comment on line 2 of that file |
| Google Form embed | `template.charity-applications.php` |
| S3 object URL | `https://s3-eu-west-1.amazonaws.com/twooceans-marathon/wp-content/themes/omtom/assets/images/logos/omtom.png` in the contact email |
| CloudFront | `d19jcneif5xmtf.cloudfront.net` |

XML-RPC remains in core (`xmlrpc.php`). Plugin Disable XML-RPC Pingback is vendored to remove the pingback method if that plugin is active.

## Auth

- WordPress login, cookies, and nonces are core (`wp-login.php`, salts in `wp-config.php`).
- Theme settings require capability `manage_options` (`classes/theme-options/menus.php`).
- Newsletter, contact, and newsletter-report AJAX actions are hooked to both `wp_ajax_` and `wp_ajax_nopriv_`. The handlers that were read do not call `check_ajax_referer`.
- No custom user role, OAuth server, or SSO integration is in the theme.

## Background work

- `wp-cron.php` is stock WordPress. No custom cron schedule was found in the theme.
- No queue worker, no `wp-content` drop-in for Redis or Memcached, was found.
- Instagram is fetched during the request that calls `pull_instagram_feed()`, not by a scheduled job in the code that was read.

## Storage

- Database: MySQL database `twooceansmarathon`.
- Uploads: WP Offload Media is configured in `wp-config.php` with AWS keys (omitted). The contact email points at bucket `twooceans-marathon` in `eu-west-1`.
- Theme static files are referenced on CloudFront. The same files also exist in git under the theme.
- Newsletter CSV files are written with `wp_upload_dir()['path']`.

## Deployment and infrastructure

No pipeline, Docker Compose, or git-ftp script is in the repository.

Evidence of a former or intended runtime, not proof of the current one:

- Document root hint `/var/www/html/twooceansmarathon/` in `wordfence-waf.php`.
- Commented RDS instance in `eu-west-1`.
- S3 and CloudFront in `eu-west-1` (S3 URL host is `s3-eu-west-1`).

How code is released (FTP, rsync, manual copy) is **Unknown / Requires Confirmation**.

OVER-310 named `sonar-project.properties` and `sonarqube_scan.bat` on the scan host. They are not in `0ceed11`.

## Security observations

These are facts about the tree. They are not an exploit procedure.

1. `wp-config.php` is in git and contains an empty root database password, a commented production-style database password, WordPress salts, and AWS access keys inside `AS3CF_SETTINGS`. Anyone with repository access has those values. They should be rotated. This document does not repeat them.
2. `classes/forms/forms.php` hardcodes a Touchbase Pro bearer token and list id.
3. `assets/js/maps.js` line 2 comments out a Google Maps API key. The live key is intended to come from the `api-google-maps` option.
4. `phpinfo.php` is a public information disclosure if the web root is deployed as committed.
5. `newsletter_reporting` is available without login and interpolates `fromDate` and `toDate` into SQL.
6. Contact and subscription actions have no nonce in the handler.
7. `FS_METHOD` `direct` allows WordPress to write files as the PHP user.
8. WordPress 4.9.21 and the vendored plugin versions date from the 2018–2022 period. The last commit is 2022-09-30. Currency of security patches relative to 2026 is **Unknown** beyond the version numbers in git.
9. `.gitignore` does not ignore `wp-config.php` or uploads.

## Development and testing

- Theme styles: edit `assets/scss/*.scss`. Compiled CSS is `assets/css/tom.css` and `admin.css` (source maps `tom.css.map`, `admin.css.map` are in the theme). The enqueue URLs do not point at the working copy; they point at CloudFront.
- No PHPUnit, npm test, or CI definition is in the repo.
- `readme.html` is stock WordPress. Theme `README.txt` describes installing OMTOM from the WordPress.org theme search. That install path does not match this repository, which vendors the theme inside a full WordPress tree. Provenance: **Documented** install steps vs **Implemented** layout. **Inferred:** the README was carried over from a theme scaffold and is not the deploy procedure for this repo.

## Git history relevant to operations

| Commit | Date | Author | Effect |
|---|---|---|---|
| `aa65c90` | 2022-09-29 | Jaco Brits | Initial commit |
| `c5cd0fb` | 2022-09-29 | Rachel Mogano | First commit (WordPress tree) |
| `2f1a52b` | 2022-09-30 | liam-ovd | Cookie set from footer on load |
| `0ceed11` | 2022-09-30 | liam-ovd | Cookie expiry lengthened to 10 years |

Only branch: `master`.
