# PRD — procon8

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

There is no prior PRD in the repository or in Silicon Ark. Requirements below were recovered from code, config, and git history. Provenance is marked on each item.

Content that editors enter in WordPress (pages, listings, products, menus, forms) is not in git. Requirements that depend on that content are marked **Unknown / Requires Confirmation**.

Credentials, salts, and the ACM certificate ARN are present in git. They are not copied into this PRD.

## Product purpose

The repository packages a WordPress directory site for deployment to AWS Elastic Beanstalk.

The names that the implementation uses for the site are **Discover** (database name and user) and **Discover Kasi** / **DiscoverKasi** (commit message, deleted S3 tag `OWNER`, RDS cluster name prefix `discoverkasi-cluster`, historical filesystem account `discoverikasico`). The repository and the CloudFormation application are named **procon8**.

Provenance: **Implemented** (`wordpress/wp-config.php`, commit `52b6bc59`, `s3bucket.json` before it was deleted, `elasticbeanstalk.json`). Which string is the public brand, and who the customer is, is **Unknown / Requires Confirmation**.

The directory behaviour comes from the PixelGrade Listable theme (version 1.9.2): "a directory WordPress theme that will help you create and manage a local or global listings site." The theme templates assume WP Job Manager listings (`job_listing`), not a custom post type written for this repo. Provenance: **Implemented** (theme header and `wordpress/wp-content/themes/listable/functions.php`).

`README.md` describes how to copy the Beanstalk deploy script into another project. That is a deploy note, not the product purpose. Provenance: **Documented** in `README.md`.

## Users and roles

| Role | What they can do | Evidence | Provenance |
|---|---|---|---|
| Public visitor | Reads the public site. Can search listings if WP Job Manager templates are in use. Can submit forms, book, or buy only if the matching plugin is active and a page embeds it | Listable front end; plugins are vendored | Inferred. Actual pages are not in git |
| Listing owner | If WP Job Manager is active, a logged-in user sees "Your listings" and can edit, delete, relist, or mark filled, subject to that plugin's settings | `themes/listable/job_manager/job-dashboard.php` | Vendor template. Whether the plugin is active is Unknown |
| WordPress administrator | Full `wp-admin`. Admin is forced onto SSL | `FORCE_SSL_ADMIN` in `wp-config.php`; WordPress core | Implemented |
| Editor, author, contributor, subscriber | Standard WordPress capabilities | WordPress core | Implemented by core. Which roles are in use is Unknown |
| Menu-restricted user | If Nav Menu Roles is active, menu items can be limited by role | Plugin is vendored (`nav-menu-roles` 1.8.6) | Vendor behaviour. Activation is Unknown |
| Shopper | If WooCommerce or Ecwid is active, can use that shop | Both plugins are vendored | Inferred. Which shop is live is Unknown |

No custom role is registered by the child theme. Sign-in plugins present, any of which might be the login UI if active: Theme My Login 7.0.5, Login With Ajax 3.1.7, SocialAll Social Login 2.3, and Jetpack 6.0 (which includes SSO). Which one is used is **Unknown**.

Who holds the administrator account is **Unknown**.

## Main workflows

### W1 — Read the public site

Trigger: a visitor opens the site.

Behaviour that is in code:

- WordPress serves the active theme. The child theme enqueues Listable's `style.css` and then its own `style.css`.
- On the home page, elements matching `.home span.category-count` are hidden.
- Pretty permalinks are written in `.htaccess` (request path rewritten to `index.php` when the file or directory does not exist).
- If the request is a listing archive and JavaScript is off, the Listable job-filter template tells the visitor that JavaScript is required to view listings. That string is theme copy. It only runs if those templates are used.

Provenance: **Implemented** in the child theme, `.htaccess`, and Listable templates. The active theme and the home page layout are database settings. **Unknown**.

### W2 — Find a listing

Trigger: a visitor uses the listing search.

Listable registers front-end strings and body classes for the `job_listing` post type and the taxonomies `job_listing_category`, `job_listing_tag`, and `job_listing_region`. Empty results use the string "No results".

Provenance: **Implemented** in `themes/listable/functions.php`, conditional on WP Job Manager being active. The plugin is in the tree (`wp-job-manager` 1.30.2). Activation is **Unknown**.

### W3 — Submit a listing

Trigger: a logged-in user submits the WP Job Manager form, if that page exists.

The theme's `job-submitted.php` has two messages: the listing is visible immediately, or it stays hidden until approved. Which message shows depends on the plugin's moderation setting, which is not in git.

The theme also ships templates for paid listing packages (`wc-paid-listings/`) and bookmarks (`wp-job-manager-bookmarks/`). Those plugins are **not** in `wp-content/plugins/`. The templates do nothing unless those plugins are installed another way. Provenance: **Implemented** as templates only.

### W4 — Buy or book

Trigger: a visitor uses a shop or booking page, if one is published.

The tree contains WooCommerce 3.3.4, WooCommerce Bookings 1.10.11, WooCommerce Accommodation Bookings 1.1.2, Ecwid 5.6.1, and four other booking plugins (see `features.md`). Listable declares WooCommerce support and overrides WooCommerce templates, including a custom place-order partial.

Provenance: **Implemented** as vendored code. Which product type is actually sold or booked is **Unknown / Requires Confirmation**.

### W5 — Sign in to wp-admin

Trigger: an administrator opens wp-admin.

- `FORCE_SSL_ADMIN` is true.
- If `HTTP_X_FORWARDED_PROTO` is `https`, `HTTPS` is set to `on`, so SSL detection works behind the load balancer.
- `wordpress/wp-admin/css/login.css` changes the secondary button focus border from `#5b9dd9` to `#4CAF50`. The primary button colour is unchanged.

Provenance: **Implemented**.

### W6 — Deploy from Bitbucket

Trigger: a commit is pushed to `master`.

The pipeline on `master` installs `zip` and `boto3==1.3.0`, zips `wordpress/*` to `/tmp/artifact.zip`, and runs `beanstalk_deploy.py`. The script uploads that zip to S3 under the key `wordpress.zip`, creates an Elastic Beanstalk application version labelled with the UTC timestamp `YYYYMMDDHHMMSS`, waits 5 seconds, and updates the environment named by `APPLICATION_ENVIRONMENT`.

If the S3 upload, version create, or environment update fails, the script exits 1. There is no retry and no health wait beyond the 5 second sleep.

Provenance: **Implemented** in `bitbucket-pipelines.yml` and `beanstalk_deploy.py`. The variable values are **Unknown**.

The `Dev` branch has its own pipeline that zips `test/*` and a different S3 key pattern (`APPLICATION_NAME` + `/` + timestamp). That branch does not contain the WordPress site. Provenance: **Implemented** on `origin/Dev` at `04a2b149`.

## Functional requirements

### FR-001 — WordPress site

The system is WordPress 5.2.1 with table prefix `wp_`, charset `utf8mb4`, and `WP_DEBUG` false.

Evidence: `wordpress/wp-includes/version.php`, `wordpress/wp-config.php`.

Implementation: Implemented.

Confidence: High.

### FR-002 — Directory listings

The presentation layer is Listable 1.9.2 plus a child theme. Listings are the `job_listing` type from WP Job Manager when that plugin is active.

Evidence: theme headers; `themes/listable/functions.php`; `wp-content/plugins/wp-job-manager/wp-job-manager.php`.

Implementation: Implemented in code. Active in production: Unknown.

Confidence: High that the code is present. Low that it is the live configuration.

### FR-003 — Hide home category counts

On the home view, category count badges are not shown.

Evidence: `themes/listable-child-master/style.css` rule `.home span.category-count { display: none !important; }`.

A separate plugin, Hide products count 0.6, removes the WooCommerce subcategory count HTML. Its directory is named `hide-products-count.php_`. WordPress still scans that directory for a plugin header. Whether it is active is a database option.

Implementation: Child CSS is Implemented. Plugin activation is Unknown.

Confidence: High for the CSS rule.

### FR-004 — Commerce and bookings available to turn on

The repository includes WooCommerce, WooCommerce Bookings, WooCommerce Accommodation Bookings, Ecwid, WP Booking Calendar 8.0, Pinpoint Booking System 2.6.4, Advanced Booking Calendar 1.4.9, and WP Simple Booking Calendar 1.5.1.

Evidence: plugin headers under `wordpress/wp-content/plugins/`.

Implementation: Present. Which are active: Unknown.

Confidence: High that the files exist.

### FR-005 — Forms, FAQs, calls to action, landing pages

Contact Form 7 4.8.1, WPForms Lite 1.4.7.1, Quick and Easy FAQs 1.1.2, Calls to Action 3.3.3, and Landing Pages 2.6.3 are in the tree.

Implementation: Present. Published forms and pages: Unknown.

### FR-006 — Media offload to S3

`wp-config.php` sets `AS3CF_AWS_USE_EC2_IAM_ROLE` to true. The plugin WP Offload Media (`amazon-s3-and-cloudfront` 2.2) is vendored. Uploads are not in git.

An EFS mount that symlinked `wp-content/uploads` to `/mnt/efs/uploads` was added with the WordPress tree and deleted the same day (`d2e8b542`, message "edited", change type delete).

Implementation: IAM flag and plugin are Implemented. Offload bucket and whether uploads succeed on more than one instance: Unknown.

### FR-007 — Database

WordPress connects to a database named `Discover` as user `Discover` on an RDS cluster endpoint in `eu-west-1` whose cluster name begins with `discoverkasi-cluster`. The password and the eight salt constants are committed in `wp-config.php`. This PRD does not repeat them.

Evidence: `wordpress/wp-config.php`.

Implementation: Implemented in config. Whether that host still accepts the committed password: Unknown / Requires Confirmation.

### FR-008 — HTTPS in front of the app

Admin SSL is forced. The app treats `X-Forwarded-Proto: https` as HTTPS.

`loadbalancer-sg.config` defines an HTTPS listener on port 443 with an ACM certificate in `eu-west-1`, forwarding to an HTTP target group on port 80. Health check path is `/readme.html`, interval 15 seconds, timeout 5 seconds, healthy threshold 3, unhealthy threshold 5. The security group allows 443 from `0.0.0.0/0`. VPC id, two subnet ids, and the certificate ARN are hardcoded in that file.

Implementation: Implemented in the extension file. Whether the pipeline packages the file, and whether Beanstalk applies an extension that is not at the bundle root: Unknown. See technical documentation.

### FR-009 — Deploy master to Elastic Beanstalk

A push to `master` packages `wordpress/*` and deploys it as S3 key `wordpress.zip` to the application and environment named by pipeline variables.

The CloudFormation template can create an application (default name `Dev-procon8`) and environment (default `dev-Procon8`) on solution stack "64bit Amazon Linux 2018.03 v2.8.9 running PHP 7.2", instance type default `t2.micro`, auto scaling 1 to 6, load balanced application load balancer, private subnets for instances, public subnets for the load balancer. Source bundle key default is `test.zip` in bucket `{BucketENV}-procon8-{region}` where `BucketENV` is `dev`, `int`, `qa`, or `prod`.

Evidence: `bitbucket-pipelines.yml`, `beanstalk_deploy.py`, `elasticbeanstalk.json`, `README.md`.

Implementation: Implemented as files. Applied in AWS: Unknown.

### FR-010 — Permalink routing

`.htaccess` enables `mod_rewrite` and sends non-file requests to `/index.php`.

Implementation: Implemented in the file. Apache must be the front server and the file must be deployed. Both are **Inferred** to be at risk: the platform is PHP on Elastic Beanstalk (Apache is the usual server for that solution stack; not re-stated in this repo), and `zip wordpress/*` skips dotfiles, so `.htaccess` may be absent from the artifact.

### FR-011 — Migration tooling

All-in-One WP Migration 6.97 and the Unlimited Extension 2.30 are vendored. `wp-content/ai1wm-backups/` has no archive, only `index.php`, `.htaccess`, and `web.config`.

The 2018 `error_log` paths under `/home/discoverikasico/public_html/` show the tree once ran on a cPanel-style account before it was committed here in 2019.

Implementation: Plugin present. A completed migration is **Inferred** from those logs. The export file is not in git.

## Integrations

| System | Purpose | Direction | Trigger | Auth as far as the repo shows | Failure behaviour |
|---|---|---|---|---|---|
| Amazon RDS | WordPress database | App to RDS | Every request | Username `Discover` and a password in `wp-config.php`. Password not copied here | WordPress cannot boot. No custom handler |
| Amazon S3 | Beanstalk source bundle; optional media offload | Pipeline uploads the zip. Plugin would upload media if configured | Pipeline on push; media on upload if the plugin is active | Pipeline uses `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. Media uses the EC2 instance role | Deploy script prints the boto error and exits 1 |
| Elastic Beanstalk | Hosts PHP | Pipeline updates an environment | Push to `master` (and, on the other branch, push to `Dev`) | Same AWS keys. IAM policy recommended by the README is `AWSElasticBeanstalkFullAccess`. The CloudFormation instance role allows `Action: *` on `Resource: *` | Deploy script exits 1. No rollback logic in the script |
| ACM / load balancer | HTTPS listener | Browser to load balancer | Visitor uses HTTPS | Certificate ARN in `loadbalancer-sg.config` | Not described in repo |
| WP Offload S3 plugin | Sends the media library to S3 | App to S3 | Media upload, if active | EC2 IAM role flag | Plugin's own behaviour. Not traced here |
| Google Analytics dashboard plugin | Analytics in wp-admin, if active | App to Google | Admin screens | Plugin settings are in the database | Unknown |
| Ecwid | Hosted shop embed, if active | Browser / app to Ecwid | Shop pages | Store id would be in the database or Ecwid settings | Unknown |
| SocialAll, Jetpack SSO | Social login, if active | Browser to the identity provider | Login | Keys are not in git | Unknown |
| Akismet 4.1.2 | Comment spam, if active and keyed | App to Akismet | Comments | API key is a database option | Unknown |
| Bitbucket Pipelines | Build and deploy | Bitbucket to AWS | Push to `master` or `Dev` | Repository pipeline variables | Step fails if apt, pip, zip, or the Python script fails |

No payment gateway keys, SMTP settings, or webhook URLs were found in project-authored files. WooCommerce can store gateways in the database. That configuration is **Unknown**.

## Constraints

- WordPress 5.2.1 and PHP platform 7.2 are both years out of support as of the evidence date. Provenance: versions in tree and template. This is an observation, not a decision to upgrade.
- `WP_DEBUG` is false. `display_errors` is Off in `.ebextensions`. `memory_limit` 256M. `max_execution_time` 60. `allow_url_fopen` On.
- The environment is defined as load balanced with up to 6 instances, while uploads are not in git and the EFS share was removed. Without S3 offload actually configured, media and session files would be local to one instance. Provenance: **Inferred** from the template plus the deleted EFS file plus the offload flag.
- Pipeline image is `python:3.5.7` and boto3 is pinned to 1.3.0 (2016). Provenance: **Implemented**.
- There is no automated test suite for this site. Provenance: **Implemented** (no project tests; only vendor `phpunit.xml.dist` files inside the S3 plugin's dependencies).

## Removed behaviour that is not a current requirement

- The static portfolio "Akhona Booi - Model / Brand Manager" (`test/index.html` at `05e6cc1d`) was deleted from `master` on 2019-06-18. It remains only in history. The `Dev` branch tip is a different static page titled "My Blog".
- EFS-backed uploads were deleted on 2019-08-02.
- The S3 bucket stack `s3bucket.json` (bucket name `{env}-procon81-{region}`, tags `SYSTEM=Procon8`, `OWNER=DiscoverKasi`) was deleted on 2019-07-15.
- CodeDeploy, CodeBuild `buildspec.yml`, and the early Procon8 monitoring JSON are not on either branch tip.

## Unknown / requires confirmation

- Public URL, legal name, and whether the site is still online.
- Active theme and active plugin list.
- Whether listing submission requires approval.
- Whether paid listings or bookmarks were ever installed (templates exist, plugins do not).
- Which booking plugin and which shop (WooCommerce vs Ecwid) were intended.
- Elastic Beanstalk application name, environment name, region, and S3 bucket actually used by Pipelines.
- Whether `dev`, `int`, `qa`, and `prod` stacks from the template exist. The repo has no `int`, `qa`, or `prod` branch.
- Whether the committed database password is a placeholder. The value is a short literal in `wp-config.php` and must be reviewed as a credential in git.
- Why `sonar-project.properties` and `sonarqube_scan.bat` appeared in the 2026-09-28 directory listing when they are not in git.
