# Technical documentation — procon8

Evidence date: 2026-10-06. Source: `git@bitbucket.org:overdrive/procon8.git` at `45e5ee9f` (`master`, 2019-08-02). `origin/Dev` is `04a2b149` (2019-05-10).

Credentials, WordPress salts, and the ACM certificate ARN are in the source files named below. They are not reproduced here.

## Repository layout (`master`)

10,958 tracked files.

```text
README.md
beanstalk_deploy.py
bitbucket-pipelines.yml
elasticbeanstalk.json
wordpress/
  .ebextensions/
  .htaccess
  wp-config.php
  wp-config-sample.php
  wp-admin/
  wp-includes/
  wp-content/
    themes/
    plugins/
    ai1wm-backups/
```

`wp-config.php` is tracked. There is no `.gitignore` at the tip.

## Runtime

| Item | Value | Evidence |
|---|---|---|
| Application | WordPress 5.2.1 | `wordpress/wp-includes/version.php` |
| PHP required by that WordPress | 5.6.20 | same file |
| Platform the CloudFormation template requests | 64bit Amazon Linux 2018.03 v2.8.9 running PHP 7.2 | `elasticbeanstalk.json` `SolutionStackName` |
| Document root the ini file requests | `/wordpress` | `wordpress/.ebextensions/php-settings.config` |
| Database | MySQL-compatible, name `Discover`, prefix `wp_`, charset `utf8mb4` | `wordpress/wp-config.php` |
| Database host | RDS cluster endpoint in `eu-west-1`, cluster name prefix `discoverkasi-cluster` | `wordpress/wp-config.php` |
| Pipeline image | `python:3.5.7` | `bitbucket-pipelines.yml` |
| AWS SDK in the pipeline | `boto3==1.3.0` | same file |

No Docker file, no `composer.json` at the project root, no `package.json` at the project root. Composer and npm files exist only inside vendored plugins (notably `amazon-s3-and-cloudfront` and WooCommerce extensions).

## Application structure

WordPress core is the full `wordpress/wp-admin` and `wordpress/wp-includes` tree plus the root bootstrap (`index.php`, `wp-load.php`, `wp-settings.php`, `wp-login.php`, `xmlrpc.php`, `wp-cron.php`).

Project-authored behaviour is small:

- `wordpress/wp-config.php` — DB settings, salts, `FORCE_SSL_ADMIN`, forwarded-proto HTTPS, `AS3CF_AWS_USE_EC2_IAM_ROLE`, `WP_DEBUG`.
- `wordpress/wp-content/themes/listable-child-master/` — enqueue and one CSS rule.
- `wordpress/wp-admin/css/login.css` — one colour change in core CSS (`#4CAF50` on `.login .button.button-secondary:focus`).
- Deploy and infrastructure files at the repository root and under `wordpress/.ebextensions/`.

Everything else under `themes/` and `plugins/` is third-party. Versions are listed in `features.md`.

`wordpress/wp-content/` contains `plugins/`, `themes/`, `ai1wm-backups/`, and `index.php`. There is no `mu-plugins/` directory and no `uploads/` directory.

## Themes

| Path | What it is |
|---|---|
| `wp-content/themes/listable/` | PixelGrade Listable 1.9.2 |
| `wp-content/themes/listable.zip` | Zip sitting beside the extracted theme. Contents not diffed |
| `wp-content/themes/listable-child-master/` | Child theme. Directory name is `listable-child-master`, theme name is "Listable Child" |
| `twentyfifteen`, `twentyfourteen`, `twentysixteen`, `twentyseventeen`, `twentynineteen` | Themes bundled with this WordPress release |

`listable/readme.txt` is an Underscores starter readme (stable tag 1.0.0). `listable/style.css` is version 1.9.2 and describes a directory theme. Trust the stylesheet header and the `job_manager/` templates for behaviour. The readme is stale.

## Plugins

See the tables in `features.md`. Directories under `wordpress/wp-content/plugins/` at `HEAD`:

`advanced-booking-calendar`, `akismet`, `all-in-one-wp-migration`, `all-in-one-wp-migration-unlimited-extension`, `amazon-s3-and-cloudfront`, `booking`, `booking-system`, `category-icon`, `comments-ratings`, `contact-form-7`, `cta`, `customify`, `ecwid-shopping-cart`, `google-analytics-dashboard-for-wp`, `hello.php`, `hide-products-count.php_`, `jetpack`, `landing-pages`, `login-with-ajax`, `nav-menu-roles`, `pixelgrade-care`, `pixtypes`, `quick-and-easy-faqs`, `socialall-social-login`, `string-locator`, `theme-my-login`, `woocommerce`, `woocommerce-accommodation-bookings`, `woocommerce-bookings`, `wp-job-manager`, `wp-job-manager-locations`, `wp-job-manager-products`, `wp-simple-booking-calendar`, `wpforms-lite`.

`landing-pages` and `cta` vendor old Advanced Custom Fields copies under `shared/assets/plugins/`. One of those paths (`cta/shared/assets/plugins/advanced-custom-fields-font-awesome/better-font-awesome-library/lib/fallback-font-awesome/`) is long enough that a Windows checkout needs Git `core.longpaths`.

## Configuration that always loads

`wordpress/wp-config.php`:

- `DB_NAME` and `DB_USER`: `Discover`.
- `DB_PASSWORD`: a plaintext value in git. Not copied into Silicon Ark. Treat it as a live secret until a human says otherwise.
- `DB_HOST`: RDS cluster endpoint. Not copied in full here. Region `eu-west-1`, name prefix `discoverkasi-cluster`.
- Eight `AUTH_KEY` / salt constants: present, not copied.
- `table_prefix`: `wp_`.
- `FORCE_SSL_ADMIN`: true.
- `AS3CF_AWS_USE_EC2_IAM_ROLE`: true. This constant is read by WP Offload Media (`amazon-s3-and-cloudfront`) so the plugin uses the instance role instead of stored keys.
- If `HTTP_X_FORWARDED_PROTO` is `https`, set `$_SERVER['HTTPS']` to `on`.
- `WP_DEBUG`: false.
- `ABSPATH`: directory of `wp-config.php`.

`wordpress/.htaccess` is the standard WordPress permalink block (`RewriteBase /`, front controller `index.php`).

`wordpress/wp-config-sample.php` is the stock sample. The real config is `wp-config.php`, not the sample.

## Elastic Beanstalk PHP settings

File: `wordpress/.ebextensions/php-settings.config`.

```yaml
option_settings:
  aws:elasticbeanstalk:container:php:phpini:
    document_root: /wordpress
    memory_limit: 256M
    zlib.output_compression: "Off"
    allow_url_fopen: "On"
    display_errors: "Off"
    max_execution_time: 60
    composer_options: vendor/package
```

`composer_options: vendor/package` matches an AWS example value. This repository has no root Composer project.

## Load balancer extension

File: `wordpress/.ebextensions/loadbalancer-sg.config`.

It declares CloudFormation resources inside an `.ebextensions` file:

- `AWSEBLoadBalancerSecurityGroup` — ingress TCP 443 from `0.0.0.0/0`, egress TCP 80 to `0.0.0.0/0`. `VpcId` is a hardcoded `vpc-…` id.
- `AWSEBV2LoadBalancer` — that security group, two hardcoded `subnet-…` ids.
- `AWSEBV2LoadBalancerTargetGroup` — HTTP port 80, health check path `/readme.html`, interval 15s, timeout 5s, healthy 3, unhealthy 5, deregistration delay 20s. Same VPC id.
- `AWSEBV2LoadBalancerListener` — HTTPS 443, policy `ELBSecurityPolicy-FS-2018-06`, one ACM certificate ARN in `eu-west-1`, action forward to the target group.

The certificate ARN, VPC id, and subnet ids are infrastructure identifiers committed in git. They are intentionally not copied into this document. Open the file in the source repo to read them.

Overriding `AWSEBV2LoadBalancer` from an extension can replace the environment's load balancer rather than edit it. Whether Beanstalk accepted this file is **Unknown / Requires Confirmation**.

## Why the extension files may not deploy

`bitbucket-pipelines.yml` on `master` runs:

```text
zip -r /tmp/artifact.zip wordpress/*
```

On the Debian image `python:3.5.7`, the shell glob `wordpress/*` does not include `wordpress/.ebextensions` or `wordpress/.htaccess`.

Elastic Beanstalk reads `.ebextensions` from the root of the source bundle. Even if the hidden directory were added, it would be at `wordpress/.ebextensions`, not at the bundle root, because the glob preserves the `wordpress/` prefix.

**Inferred:** the PHP ini and the load balancer resources are in git and are not on the path the pipeline publishes. Confirm with a built artifact before treating them as live.

The visible PHP files under `wordpress/` (including `wp-config.php` and `wp-admin/css/login.css`) are included, because they do not start with a dot.

## Deploy script (`master`)

File: `beanstalk_deploy.py`. Header says it is an Amazon Bitbucket Builds template, copyright 2016, Apache 2.0, author `joshcb@amazon.com`, v1.0.0.

Behaviour:

1. `VERSION_LABEL` = UTC `YYYYMMDDHHMMSS`.
2. `BUCKET_KEY` = `wordpress.zip` (constant, not the version label).
3. `upload_to_s3('/tmp/artifact.zip')` using `boto3.client('s3').put_object` to `S3_BUCKET` / `wordpress.zip`.
4. `create_application_version` for `APPLICATION_NAME`, description "New build from Bitbucket", `Process=True`.
5. Success is HTTP status 200 on that response. The comparison uses `is` (`response['ResponseMetadata']['HTTPStatusCode'] is 200`).
6. `sleep(5)`.
7. `update_environment` for `APPLICATION_ENVIRONMENT` and that version label.

Region and credentials come from the standard AWS environment variables (`AWS_DEFAULT_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`), which boto3 reads itself. The script does not take a CLI argument.

Failure: print the `ClientError` or `IOError` and `sys.exit(1)`. No retry. No wait for environment health. No delete of the previous version.

Because the S3 key is always `wordpress.zip`, each deploy overwrites the same object. Older Beanstalk versions keep pointing at that key. **Inferred** consequence: an older version label no longer has its original bytes. The `Dev` branch script does not do this; it uses `{APPLICATION_NAME}/{VERSION_LABEL}`.

## Pipeline

`master` file `bitbucket-pipelines.yml`:

- Image `python:3.5.7`.
- Branch `master`: `apt-get update`, `apt-get install -y zip`, `pip install boto3==1.3.0`, zip `wordpress/*`, `python beanstalk_deploy.py`.
- Branch `Dev` inside this same file: the same steps but `zip -j /tmp/artifact.zip Dev/*`. There is no `Dev/` directory on `master`. This step does not run on a `master` push. It would run only if this file were the pipeline definition for the `Dev` branch.

`origin/Dev` file `bitbucket-pipelines.yml` is different. It only defines branch `Dev`, and it runs `zip -r /tmp/artifact.zip test/*`. The `Dev` branch contains `test/`. That is the pipeline Bitbucket would use for a push to `Dev`.

`README.md` lists the variables and points at `elasticbeanstalk.json` as an optional way to create the Beanstalk application. The README still says to copy a `test` folder. `master` no longer has `test/`.

## CloudFormation template

File: `elasticbeanstalk.json`. Format version 2010-09-09. Description: "Deploy the Procon8 application to Elastic Beanstalk".

Parameters that matter:

| Parameter | Default | Notes |
|---|---|---|
| `BucketENV` | `dev` | Allowed: `dev`, `int`, `qa`, `prod` |
| `MinSize` / `MaxSize` | `1` / `6` | Auto Scaling |
| `WarKey` | `test.zip` | Object key of the source bundle. Pipeline uploads `wordpress.zip` |
| `KeyName` | `dev-procon8` | EC2 key pair |
| `AWSLinuxImage` | `ami-d7b9a2b1` | Description text says "The AMI of dispatcher" |
| `InstanceType` | `t2.micro` | Long allow-list of instance types |
| `ApplicationName` | `Dev-procon8` | |
| `EnvironmentName` | `dev-Procon8` | |
| `VPC`, `PublicSubnets`, `PrivateSubnets` | none | Required, no default |

Resources:

- `BeanstalkSecurityGroup` — TCP 80 from `0.0.0.0/0`, in `VPC`.
- `Application` — `AWS::ElasticBeanstalk::Application`.
- `Procon8ApplicationVersion` — source bucket `{BucketENV}-procon8-{AWS::Region}`, key `WarKey`.
- `ProconConfigurationTemplate` — PHP 7.2 solution stack, VPC, private subnets for instances, public subnets for the ELB, min/max size, environment type `LoadBalanced`, load balancer type `application`, instance profile, instance type, key name, AMI, security group.
- `Environment` — uses that template and version.
- `EbAppRole` / `RolePolicies` / `EbAppInstanceProfile` — the policy statement is `Effect: Allow`, `Action: *`, `Resource: *`.

Output `URL` is `http://` plus the environment endpoint.

The template does not create the S3 bucket it reads from. A separate bucket template existed as `s3bucket.json` and was deleted on 2019-07-15. That file named the bucket `{BucketENV}-procon81-{region}` (note `procon81`, not `procon8`), with versioning and tags `SYSTEM=Procon8`, `OWNER=DiscoverKasi`, `ENVIRONMENT`, `LIFETIME=FIXED`.

No evidence in git that either template was applied.

## Removed EFS config

At commit `4ce44d8d` the tree included `wordpress/.ebextensions/EFS.config`. Commit `d2e8b542` deleted it. The commit message says "edited".

While it existed it:

- Installed `nfs-utils` and `jq`.
- Wrote `/tmp/mount-efs.sh`.
- Read `EFS_NAME` from `elasticbeanstalk/bin/get-config environment`.
- Mounted NFS 4.1 at `/mnt/efs`.
- Created `/mnt/efs/uploads` owned by `webapp`.
- Symlinked `/var/www/html/wordpress/wp-content/uploads` to that directory.
- Removed the local uploads directory.

That is the only shared-disk design in the history. It is not on `master` now.

## Auth

- WordPress cookies and nonces use the salts in `wp-config.php`.
- Admin URLs require SSL (`FORCE_SSL_ADMIN`).
- No OAuth client id for this project was found outside vendored plugins.
- Social login plugins can add other providers if they are active and configured in the database.
- Pipeline AWS keys are not in git. The README tells operators to create a dedicated IAM user.

There is no application-level session store, queue, or cron worker besides WordPress `wp-cron.php`, which runs on traffic unless something else is configured on the host. Nothing in this repo disables `wp-cron` or adds a system cron.

## Background work, queues, storage

- No project queue, no worker process, no cron config of our own.
- Object storage for deploys: S3 key `wordpress.zip` on `master`.
- Object storage for media: intended via WP Offload Media and the instance role. Bucket name is not in `wp-config.php`. It would be in the plugin's database options. **Unknown**.
- Local uploads directory is not in git. EFS share was removed.
- `ai1wm-backups` is empty of archives. `web.config` in that folder is the plugin's IIS deny file, not evidence of IIS hosting. The Beanstalk stack is Amazon Linux.

## Monitoring and security notes

- No application monitoring agent is on `master`. `Procon8_Monitoring.json` existed in April 2019 and is gone.
- Health check path in the extension is `/readme.html` (WordPress readme, HTTP 200 if the file is deployed).
- `display_errors` off, `WP_DEBUG` false.
- `allow_url_fopen` on.
- Instance role in the CloudFormation template is unrestricted (`*` / `*`).
- Database password and authentication salts are committed.
- Load balancer extension opens 443 to the world (expected for a public site) and pins a certificate ARN in git.
- XML-RPC file `xmlrpc.php` is present (WordPress default). No project code disables it.
- No SonarQube project file is in git. The onboarding scan listed `sonar-project.properties` and `sonarqube_scan.bat`; they have no history.

## Development and testing

- No PHPUnit project, no CI test step, no linter config at the root.
- The pipeline's only job is zip and deploy.
- Vendor trees contain their own `phpunit.xml.dist` and `composer.json` files. The pipeline does not run them.
- `string-locator` 2.3.0 is a development aid plugin, present in the same tree as production plugins.

## Git

| Remote | `git@bitbucket.org:overdrive/procon8.git` |
| Default branch | `master` (`origin/HEAD`) |
| Other branch | `Dev` |
| First commit | `172299e3` 2019-04-24 Gareth Bowers, "README.md created online with Bitbucket" |
| Last commit | `45e5ee9f` 2019-08-02 Ayabulela Mgxabayi |
| Commit count on `master` | 182 |

`master` is 17 commits ahead of the merge base with `Dev`. `Dev` has 2 commits not on `master`.

Authors: Ayabulela Mgxabayi (several git identities, including `ayabulela@overdrive.co.za`) and Gareth Bowers (`gareth@overdrive.co.za`).

## Historical host

`error_log` files under themes and plugins record PHP fatals in 2018 (timezone `Africa/Johannesburg`) for scripts hit directly under `/home/discoverikasico/public_html/`. Those dates are before `4ce44d8d` added the tree (2019-08-02). **Inferred:** the WordPress files were copied from that account into this repository. The logs are not a current error stream.
