# Business rules — unity

Evidence date: 2026-09-30.

Source: `git@bitbucket.org:overdrive/unity.git`, `master` at `7ac64a5`. Wiki rules apply to `master` only. They come from the TiddlyHome 0.1.3 files added in that commit (copyright BidiX, 2005–2007, and TiddlyWiki / UnaMesa in `wiki/index.html`). They are not restated in a Silicon Overdrive requirements document.

Rules are labelled **Implemented**, **Inferred**, or **Documented** (text inside the bundled wiki). Nothing here is a new product decision.

The shell and the wiki do not share users.

## Shell login

### BR-001 — Login navigates; it does not sign a person in

**Implemented.** `index.html` submits to `views/dashboard/index.html`. The username and password inputs have no `name`, so the browser does not send those values. There is no server check and no session.

### BR-002 — The browser may block an empty login

**Implemented.** Both fields are `required`. The username field is `type="email"`, so the browser applies its email check. Any accepted email and any non-empty password continue.

### BR-003 — Forgot password does nothing

**Implemented.** The link is `href="#"`.

### BR-004 — Create account points at a missing page

**Implemented.** The link is `views/register.html`. That path is not in git. `views/template/register.html` is a theme demo and is not the target of this link.

### BR-005 — The person named in the shell is fixed text

**Implemented.** `views/dashboard/index.html` always shows "Jaco Brits" and "Software Director". It does not read an account.

### BR-006 — Two logout targets

**Implemented.** The profile menu links to `/index.html`. The top bar links to `views/template/login.html`. Those are different pages. Neither clears a session, because the shell does not create one.

## Shell navigation and sample data

### BR-007 — Department names are labels

**Implemented.** The shell calls five links Dashboard Software, Dashboard Technical, Dashboard Support, Dashboard HR, and Dashboard Finance. They open theme HTML. No rule selects data by department.

### BR-008 — Dashboard Software is the theme home

**Implemented.** That item's `href` is `views/template/index.html`, not `views/dashboard/index.html`.

### BR-009 — dashboard_4 is not in the shell menu

**Implemented.** `views/dashboard/dashboard_4.html` exists and is not one of the five submenu links. HR uses `dashboard_4_1.html`.

### BR-010 — Counts and money on the shell are written in the page

**Implemented.** Examples on `views/dashboard/index.html`: "You have 42 messages and 6 notifications.", mailbox badge 16, sales "$ 406,100" and "$ 150,401". They are not calculated.

### BR-011 — Search submits a field the results page does not have at that path

**Implemented.** The form action is `search_results.html` next to the shell, input name `top-search`. The file is not in `views/dashboard/`.

## Deployment rules

### BR-012 — `master` and `develop` are the only pipeline branches

**Implemented.** `bitbucket-pipelines.yml` comments label `master` as production and `develop` as development / QA. No other branch has a step. A `test` step existed in `e1add45` and was removed in `98f1832`.

### BR-013 — The artifact is the whole tree

**Implemented.** The pipeline runs `zip -r /tmp/artifact.zip *`. That includes `SOawsCodeDeploy.pem`, assets, views, and on `master` the wiki.

### BR-014 — Files are copied to `/var/www/html/`

**Implemented.** `appspec.yml` `destination: /var/www/html/`, `os: linux`, hooks `runas: root`.

### BR-015 — The host runtime installed by this repo is httpd

**Implemented.** `scripts/install_dependencies` is `yum install -y httpd`. PHP is not installed here. **Inferred:** a host without PHP will serve the static shell and will not run `wiki/**/*.php`.

### BR-016 — httpd is started before the new revision is copied

**Implemented.** `start_server` is a `BeforeInstall` hook. There is no `ApplicationStart` hook. `stop_server` is `ApplicationStop`.

### BR-017 — The stop script's assignment is not a Bash assignment

**Implemented.** `scripts/stop_server` contains `isExistApp = \`pgrep httpd\`` with spaces around `=`. The following `if` tests `$isExistApp`. This document does not claim a particular host result; the script text is what is in git.

### BR-018 — Deploy names come from the environment

**Implemented.**

| Script | Application | Deployment group | Shared |
|---|---|---|---|
| `codedeploy_deploy.py` | `APPLICATION_NAME` | `DEPLOYMENT_GROUP_NAME` | `S3_BUCKET`, `DEPLOYMENT_CONFIG` |
| `codedeploy_deploy_develop.py` | `APPLICATION_NAME_DEV` | `DEPLOYMENT_GROUP_NAME_DEV` | same |

The values are not in the repository. **Unknown / Requires Confirmation.**

### BR-019 — A failed upload or a failed or stopped deployment exits non-zero

**Implemented.** Both Python scripts `sys.exit(1)` when upload or deploy returns false. Status `Succeeded` returns true. Status `InProgress`, `Queued`, or `Created` keeps polling. The poll loop does not call `sleep`, although `sleep` is imported. `ignoreApplicationStopFailures` is true on `create_deployment`.

### BR-020 — The committed key is not what the Python uses to call AWS

**Implemented.** Neither deploy script opens `SOawsCodeDeploy.pem`. boto3 uses its default credential chain. The key is still inside the zip from BR-013, and BR-014 copies that zip's contents to the web root.

## TiddlyHome admin init (`master` only)

These rules run only if PHP executes `wiki/_th/admin/init.php`.

### BR-021 — Init runs once

**Implemented.** If `.htaccess` already exists in the admin directory, the script prints "TiddlyHome Admin already initialize !" and exits. It does not reset the password.

### BR-022 — The admin username is the literal Admin

**Implemented.** `processForm` sets `$user = "Admin"`. The form does not ask for a username.

### BR-023 — The browser checks the init password and email

**Implemented** in the page's JavaScript: password length at least 5, confirmation must match, email length at least 3. **Implemented:** `processForm` writes the posted password and email without repeating those checks.

### BR-024 — Init creates Apache Basic auth for the admin directory

**Implemented.** It writes:

- `.htpasswd` with user Admin
- `.htgroup` with group Admin containing Admin
- `.htaccess` with `AuthType Basic`, `AuthName` "TiddlyHome Admin", `Require user Admin`
- `wiki/_th/lib/config.php` with paths and a `$ROOT_URL` built from `$_SERVER`

`fopen` is called with an unquoted second argument `w` (`init.php`). That is the source text. Behaviour depends on the PHP version and is **Unknown** without a host.

## TiddlyHome users (`master` only)

### BR-025 — Client rules for a username and password

**Implemented** in `wiki/_th/lib/userForm.php` JavaScript, before submit:

- Username length at least 3.
- Username must match `^[a-zA-Z0-9][a-zA-Z0-9\.\-]{1,50}$`. The alert calls this a "site id".
- Password at least 5 characters when a password field is on the form.
- Password and confirmation must match when both fields exist.
- Email length at least 3 when an email field exists.

### BR-026 — The server does not repeat the client pattern checks

**Implemented.** `process()` for `action=new` rejects only an existing username, then `setUser`. It does not check length, the character class, `password_check`, or email length.

### BR-027 — A new user must not already exist

**Implemented.** Message: "user '$user' already exist. Try an other name".

### BR-028 — Password storage depends on the OS string

**Implemented.** `bidix_crypt` in `wiki/_th/lib/Htpasswd.php`: if `php_uname()` matches `windows` via `eregi`, it returns the password unchanged. Otherwise it returns `crypt($passwd, $salt)`.

**Documented** in the bundled wiki text: on Windows, passwords are not encrypted, for Apache compatibility. `eregi` itself is not available in PHP 7 and later. Host PHP is **Unknown**.

### BR-029 — Changing or deleting a user checks the password

**Implemented.** `change password` calls `setPassword`. Delete and display paths call `verifyPassword`. Verify compares `bidix_crypt` of the typed password, salted with the stored value, to the stored value. On the Windows branch of `bidix_crypt`, that comparison is plaintext equality.

### BR-030 — The user list can print stored passwords

**Implemented.** `Htpasswd` has a display helper that writes the user, the stored password field, and the email into the page. Who can open that page depends on the admin `.htaccess` after init (`Require user Admin`).

## TiddlyHome sites (`master` only)

### BR-031 — Creating a site requires a matching owner password

**Implemented.** `siteForm.php` `action=new`: if `verifyPassword($owner, $password)` is not true, the message is "Owner or Password does not match for '$owner'". The site is not created.

### BR-032 — A site name cannot be reused

**Implemented.** If `Site::exists()` (the site directory already has an htaccess file), the message is "site '…' already exists."

### BR-033 — The site directory is the raw site name

**Implemented.** `Site::inSite()` runs `mkdir($this->name)` with no character whitelist in that function. The browser script on the site form only checks that site, owner, and password are non-empty. It references `the_form` without looking it up by id (unlike the user form).

### BR-034 — The owner is always a member of the site group

**Implemented.** `Site::setGroup()` prepends the owner to the group string when a word-boundary match does not already find the owner, then stores the group under the site name in the admin htgroup file.

### BR-035 — Access is private or public

**Implemented.** Checkbox `private` value `on` sets access `private`. Otherwise access is `public`.

- Private template: `Require group` for the site name on the directory, plus `Options +Indexes`.
- Public template: Basic auth is still declared; `Require group` is limited to `<files store*>`.

### BR-036 — A new site starts from empty.html

**Implemented.** `Site::$flavour` defaults to `empty.html`. `init()` copies that template to the site `index.html` and links `download.php`, `logout.php`, `news.php`, `proxy.php`, `store.php`, and `storeTiddler.php`.

### BR-037 — Delete, change group, and change access require the owner and the password

**Implemented.** Those actions check `$site->owner != $owner` or a failed `verifyPassword`, and then refuse. The failure message uses `$user`, which is not the owner variable in that function (`siteForm.php`).

### BR-038 — Admin site management is reserved to Admin by the file header and by Apache

**Documented** in the comment at the top of `wiki/_th/admin/sites.php`: "Manage all sites - Reserved to Admin". **Implemented** gate after init: the admin `.htaccess` `Require user Admin`. The PHP itself, once `config.php` loads, does not check a role flag.

### BR-039 — Sites are listed as directories that do not start with `.` or `_`

**Implemented.** `sites.php` `dir_list` skips names matching `/^[_.]/` and skips non-directories. It prints owner, access, mtime, and links for display, delete, change group, and change access.

## Rules that are not enforced

- The shell does not require the TiddlyHome Admin password.
- Department dashboards do not restrict by role.
- No rate limit, captcha, or row-level security appears in this repository.
- Sample invoices, mail, and projects in the theme are not business records.

## Unknown / Requires Confirmation

- Whether a failed shell login was ever required.
- Whether TiddlyHome was meant to replace the theme blog pages or only to be tried (`7ac64a5` message is "wiki testing").
- The real AWS names behind the environment variables.
- Whether production should be `master` (pipeline comment) or the branch whose footer says DEVELOPMENT (`develop`).
- Whether the 1996 copyright year is a business fact or leftover theme text. The repository only shows the string.
