# Technical documentation — checkin.mypass.zone

Evidence date: 2026-10-19. Source: `master` at `09abf7a` (2022-07-01). Differences on `prod` at `a78d45a` (2022-11-30) and `ui-changes` at `be0f89bb` (2022-08-17) are called out.

## Stack

| Item | Evidence |
|---|---|
| Language | PHP. Front controller turns on `E_ALL \| E_STRICT` and `display_errors` |
| Framework | Zend Framework 1, vendored in `library/Zend`. `Zend_Application` in `public/index.php` |
| UI | Server-rendered `.phtml`. Limitless theme (HTML copied from `demo.interface.club`, HTTrack comment dated 2018-02-02). Bootstrap, jQuery, Font Awesome 5.3.1 from a CDN on the login page. Google font Roboto |
| Database | `pdo_mysql` through `Zend_Db_Table` |
| Web server | Apache. `scripts/install_dependencies` runs `yum install -y httpd`. `start_server` / `stop_server` use `service httpd` |
| Timezone | `Africa/Johannesburg` set in `public/index.php` |
| Locale resource | `af_ZA` in `application.ini`. UI copy that was read is English |
| Crypto in app code | `md5` for passwords and reset keys. `mcrypt_create_iv` for registration refs and keys. PHP builds that removed `mcrypt` cannot run those paths |

No `composer.json`, no automated test directory, no README, no Dockerfile.

`public/dompdf` is present. The collection controllers that were read do not call it. **Unknown** whether a permit view still includes it.

## Request path

Root `.htaccess` rewrites every URI that does not already start with `/public/` into `public/`. The HTTPS-to-www rules in that file are commented out.

`public/.htaccess` serves real files and directories, and rewrites everything else to `index.php`.

`index.php`:

- Defines `APPLICATION_PATH` as `../application`.
- `APPLICATION_ENV` from the environment, otherwise `production`.
- Defines `UPDATE_PATH` as `public/archives/` (the `realpath` result; if `archives` is missing the constant becomes empty because `realpath` fails).
- Puts `library/` on `include_path`.
- Bootstraps `application/configs/application.ini` and runs.

A commented block at the top of `index.php` would stop the request and show a maintenance image. It is not active.

`application/Bootstrap.php` exists and its `_initDbAdapter` is empty. Database comes from the ini resource.

`resources.frontController.moduleDirectory` is `application/modules`. The only module is `default`.

`PG_Controller_Plugin_LayoutPicker` (`library/PG/Controller/Plugin/LayoutPicker.php`) sets the layout name to the module name on `preDispatch`, so the layout used is `default` (`application/layouts/default.phtml`). `application.ini` also sets `resources.layout.layout = default`. Other layout keys in the ini (`admin`, `union`, `referee`, `gir`, `accreditations`) have no matching layout files. `application/layouts/layout.phtml` exists and is not the name the plugin selects. On `prod`, that unused file's title is also changed to `MyPASS Checkin`.

A second bootstrap class, `Default_Bootstrap`, lives at `application/modules/default/controllers/Bootstrap.php` and redirects to HTTPS when the port is not 443. Zend looks for module bootstraps at `modules/{name}/Bootstrap.php`, not under `controllers/`. **Inferred:** the redirect does not run from this path.

## Configuration

`application.ini` sections: `production`, and `staging`, `testing`, `development` inherit production. All environments keep `display_startup_errors`, `display_errors`, and `displayExceptions` at `0`. That does not override the `ini_set('display_errors', 1)` in `public/index.php` and at the top of the controllers.

Session cookie lifetime: `864000` seconds.

Database resource (production, inherited by the other sections):

| | `master` | `prod` |
|---|---|---|
| Host | `localhost` | `db.footprintapp.net` |
| Database name | `teamwizz_SARU` | `teamwizz_SARU` (unchanged) |
| Adapter | `pdo_mysql` | `pdo_mysql` |
| Username | `teamwizz_ADMIN` | same |

The password is in the file on both branches. It is not repeated here.

`[production]` is the default because `index.php` falls back to `production` when `APPLICATION_ENV` is unset. There is no separate host or database for staging, testing, or development inside the ini.

Memory limit set inside controllers is `180M` on `master`. On `prod`, `IndexController` and `LoginController` set `1024M`.

**Security note for reviewers.** The ini file and `LoginController` / `RegisterController` contain credentials (database, SMTP, shared PIN). Silicon Ark does not copy the secret values. Rotation is a human action.

## Controllers and routes

Zend default routing: `/{controller}/{action}/{param}/...`. Module `default` is omitted from the URL.

| URL prefix | Class | Session rule |
|---|---|---|
| `/` and `/index/...` | `IndexController` | `EVENT.EID` or redirect `/login/` |
| `/login/...` | `LoginController` | Layout disabled. Logout clears auth and session. Picture and remote upload do not check the event session |
| `/register/...` | `RegisterController` | No session required. Layout disabled |
| `/permits/...` | `PermitsController` | `EVENT.EID` or redirect `/login/` |
| `/users/...` | `UsersController` | `Zend_Auth` identity or redirect `/login/` |
| `/company/...` | `CompanyController` | `Zend_Auth` identity or redirect `/login/` |
| `/error/...` | `ErrorController` | Standard ZF1 error handler |
| File `EventsController.php` | Class is `Companies_EventsController` | Not the class name Zend loads for controller `events` |
| File `OrgController.php` | Class is `Company_OrgController` | Same mismatch |
| `/organisation/...` | `OrganisationController` | Actions empty |
| `/authon/...` | `AuthonController` | Actions empty |

`LoginController::init` disables the layout, so the shell is not used on login, forgot, or reset. Those pages are full HTML documents.

## Collection data path

`Model_AccreditationList` is bound to table `Accreditation_List`. `getApplications` does not read that table. It selects from `Accreditation_Collections` with integrity check off:

- `where AC = '{session EID}'`
- optional search `where` built by concatenating `srch`
- order `organisation`, `FirstName`, `Surname`

`IndexController::getapplicantsAction` groups the result by column `ID`.

`IndexController::collectAction` updates `Accreditation_Applicants_Services` (`Model_AccreditationApplicantsServices`):

```text
Status = {status parameter}
LastUpdate = now
Collected = now
where AASID = '{id parameter}'
```

The id and status are concatenated into the `where` and the status is passed as a bound column value through `update()`. The `where` string is not a bound parameter.

No `CREATE` or view definition for `Accreditation_Collections` is in the repository.

## Models that the check-in and supplier code use

| Class | Table |
|---|---|
| `Model_AccreditationList` | `Accreditation_List` (list methods). `getApplications` reads `Accreditation_Collections` |
| `Model_AccreditationApplicantsServices` | `Accreditation_Applicants_Services` |
| `Model_AccreditationPermits` | `Accreditation_Permits` |
| `Model_AccreditationLoadingServices` | `Accreditation_Load_Services_Types` |
| `Model_AccreditationLoadingZones` | `Accreditation_Loading_Zones` |
| `Model_AccreditationGates` | `EventGates` |
| `Model_AccreVehicleTypes` | `vehicletypes` |
| `Model_AccreCompanyTypes` | `Accre_Company_Types` |
| `Model_CompanyProfile` | `Companies_Profile` |
| `Model_CompanyEvents` | `Company_Events` |
| `Model_UserProfile` | `Company_Members` |
| `Model_Country` | `countries` |
| `Model_BBEELevel` | `BBEE_Level` |
| `Model_BBEETypes` | `B_BBEE_Types` |

`LoginNow` authenticates table `CompaniesLogin` (email, password column). That table has no model class of its own.

Other model files (fixtures, squads, tournaments, food, news, unions) are unused by the controllers in this repository. See `features.md` F-17.

`PermitsController::createAction` calls `getEvent` on `Model_AccreditationList`. `EventsController` calls `getEvents`. Neither method is defined. Those actions cannot complete.

## Auth

Two mechanisms exist and they are not connected:

1. PIN. Compares one string. Writes `Zend_Session_Namespace('EVENT')->EID`. Does not write `Zend_Auth`.
2. `Zend_Auth_Adapter_DbTable` on `CompaniesLogin`, used only by `LoginNow`, which the login form does not call. That method writes event id `22`.

Password reset and registration talk to `Company_Members`, not to `CompaniesLogin`. **Inferred:** a reset password may not be the credential `LoginNow` checks, because the adapter table is `CompaniesLogin` and the update table is `Company_Members`. Whether those are the same physical table under two names is **Unknown / Requires Confirmation**.

Staff and company controllers treat any `Zend_Auth` identity as sufficient. No role check was found.

## Mail

`LoginController::sendMail` and `RegisterController::sendMail`:

- SMTP host `mail.sarugby.co.za`, port `25`, auth `login`
- From `accreditation@sarugby.co.za`, subject `SARugby Notification`
- Username and password are hardcoded. Not copied here.

Registration confirmation URL host is `supplier.sarugby.online`. Reset URL host is `$_SERVER['SERVER_NAME']` of this app.

## External HTTP

On `prod` only, the login page calls `https://footprintapp.net/api/mypass/gettermsandconditions/` and expects JSON with `termsandconditions`.

`master` links terms to `http://www.sarugby.co.za/` and does not call that API.

Login pages on both tips load `https://cdn.ywxi.net/js/1.js`.

## Files and storage

`.gitignore` ignores `public/archives/` and `public/files/pics/`.

Image writes:

- Company: `UPDATE_PATH/companies/{CID}_image.jpg`
- Staff: `UPDATE_PATH/accreditation/{id}_image.jpg`
- Remote company upload: same companies path, `cid` from the request, no session check

`/login/companypic/id/{id}` reads the company file or returns a built-in GIF payload with `Content-type: image/gif`.

## Deployment

`appspec.yml` (CodeDeploy format 0.0):

- Files: `/` → `/mnt/data/www/html/mypass.zone/checkin.mypass.zone/`
- Owner and group `apache`
- Hooks, all `runas: root`, timeout 300: `scripts/install_dependencies`, `scripts/remove_archives.sh`, `scripts/start_server`

`bitbucket-pipelines.yml`:

- Image `python:3.5.1` on `master`, `python:3.5.7` on `prod` and `ui-changes`
- Branches `qa` and `prod` only
- Installs `zip` and `boto3==1.3.0`, zips the working tree to `/tmp/artifact.zip`, runs `codedeploy_deploy.py`
- AWS settings come from variables `QA_*` or `PROD_*`: access key, secret, region, application name, deployment group, S3 bucket

`codedeploy_deploy.py` is the AWS sample Bitbucket Builds template (header v1.0.0). It uploads the zip to S3 and creates a CodeDeploy revision. Bucket and application name are environment variables.

Remote heads at inspection:

- `master` `09abf7a`
- `prod` `a78d45a`
- `ui-changes` `be0f89bb`

No `qa` head.

`scripts/stop_server` is not referenced by `appspec.yml`. The assignment `isExistApp = \`pgrep httpd\`` is not valid Bash for the test that follows.

## Process and quality tools

No PHPUnit, no CI test step, no linter config, no README.

The onboarding brief listed `sonar-project.properties` and `sonarqube_scan.bat` at the repository root. They are not in `master` at `09abf7a`. Provenance of those filenames: the scan-host snapshot in the issue, not Git.

`public/error_log` is in the tree.

## Security behaviour worth a review

Recorded so a later change can be judged. Not a procedure.

- Shared PIN, database password, and SMTP password are in Git.
- `display_errors` is forced on in the front controller and in feature controllers.
- Search text and the collect id are concatenated into SQL.
- Collect does not check that the service row belongs to the session event, and it writes the status parameter it is given.
- `IndexController::testAction` prints a member row for a fixed identity number to anyone with the event session.
- `/login/uploadremotecompany` writes an image for a posted company id without the event session.
- Password reset email includes the new password.
- Registration and reset build `where` clauses from request strings.
- `mcrypt` and `md5` are the only password and key mechanisms found.

## Related Silicon Ark projects

`track.mypass.zone` is a separate repository with the same framework, the same database name on `db.footprintapp.net`, and a PIN dashboard for vehicle permits rather than accreditation collection. Its documented session event on `master` is `367`, which is the id `ui-changes` uses here and is not the id on this repo's `master` (`245`) or `prod` (`479`). No file in this repository names `track.mypass.zone`. Whether they share `teamwizz_SARU` in production is **Unknown / Requires Confirmation**.

## Requires confirmation

- Which branch is deployed, and whether the live database host is `localhost` or `db.footprintapp.net`.
- Definition of `Accreditation_Collections` and of column `ID`.
- Whether `CompaniesLogin` and `Company_Members` are the same table.
- Whether `public/dompdf` is still invoked from a view that was not opened.
- Whether the Sonar files exist only on the scan host.
