# Business rules — skeleton

Evidence date: 2026-10-03. Source: `https://bitbucket.org/overdrive/skeleton.git`, `master` `43b5796` (2020-02-27).

Provenance labels: **Documented**, **Implemented**, **Inferred**, **Planned**, **Unknown**.

No business-rules document existed in the repository or in `silicon_ark/skeleton/` before this file.

This repository does not encode user, role, billing, or workflow rules. The README names rules that the code does not enforce. The rows below keep those apart. Framework defaults are included only where they change what a request does if the starter is run as committed. They are starter settings, not a written Overdrive policy.

## Rules the application enforces

### BR-001 — The only page is the login view

**Implemented.** `Home::index` returns `view('login/login')` for `GET /`. The view prints `jaco`. There is no password, no 2FA, no redirect, and no session write.

Evidence: `app/Controllers/Home.php`, `app/Views/login/login.php`, `app/Config/Routes.php`.

### BR-002 — Auto-routing is on

**Implemented.** `$routes->setAutoRoute(true)`. Any public controller method under `App\Controllers` becomes a URL. Today the only controller is `Home`, with public method `index`. A method added later is reachable without a new route line.

Evidence: `app/Config/Routes.php`.

Default namespace `App\Controllers`. Default controller `Home`. Default method `index`. URI dashes are not translated into underscores (`setTranslateURIDashes(false)`). No custom 404 override is passed to `set404Override()`.

### BR-003 — HTTP entry requires PHP 7.2 or newer

**Implemented** in `public/index.php`. **Documented** in `README.md` and `composer.json`.

Below 7.2 the front controller dies with a fixed message and does not boot the framework.

### BR-004 — spark refuses php-cgi

**Implemented.** If `php_sapi_name()` starts with `cgi`, `spark` dies with `The cli tool is not supported when running php-cgi. It needs php-cli to function!` and does not boot.

### BR-005 — Apache denies HTTP access to app and writable

**Implemented.** Both `.htaccess` files require `all denied` when `authz_core_module` is present, otherwise `Deny from all`.

This does not apply unless the server honours `.htaccess`. Other servers: **Unknown / Requires Confirmation**.

### BR-006 — Public requests for non-files go to index.php

**Implemented** in `public/.htaccess`, when `mod_rewrite` is available:

- A trailing slash on a non-directory redirects to the path without the slash (`R=301`).
- When HTTPS is off, host `www.<rest>` redirects to `http://<rest><uri>` (`R=301`). The rule does not run when HTTPS is on, and the substitution hard-codes `http`.
- A URI that is not an existing file or directory rewrites internally to `index.php/<uri>`.
- A non-empty `Authorization` header is copied to `HTTP_AUTHORIZATION`.

If `mod_rewrite` is absent, `ErrorDocument 404` is `index.php`.

`$indexPage` in `app/Config/App.php` remains `index.php`. The rewrite and the index-page setting are both committed. How generated URLs look is **Inferred**: the framework will include `index.php` until that property is blank. Confirm against a running app. **Unknown** until then.

### BR-007 — Testing uses the tests database group

**Implemented.** `Config\Database` sets `$defaultGroup` to `tests` when `ENVIRONMENT === 'testing'`. The `tests` group is SQLite3, database `:memory:`, host `127.0.0.1`, table prefix `db_`, empty username and password. `phpunit.xml.dist` sets `database.tests.database` to `:memory:` and `database.tests.DBDriver` to `SQLite3`, and sets `app.baseURL` to `http://example.com` for the test process.

If environment variable `DB` is set and `tests/travis/Database.php` exists and defines `$dbconfig[$group]`, the tests connection is replaced. `tests/travis/Database.php` is not in this commit, so that branch does nothing here.

Purpose of the switch, from the comment in `Database.php`: avoid writing the live database during PHPUnit. **Documented** in that comment. **Implemented** in the constructor.

### BR-008 — .env is not committed

**Implemented.** `.gitignore` lists `.env`. **Documented** in the README ("Remember to put .env in git ignore").

## Starter settings that are not product rules

These values are in committed config. Nothing in `app/Controllers` or `app/Models` branches on them. Changing them would change framework behaviour. They are not evidence of an Overdrive business decision.

| Setting | Committed value | Evidence | Provenance |
|---|---|---|---|
| Base URL | `http://localhost:8080/` | `app/Config/App.php` | **Implemented** |
| Locale | `en` only; negotiate locale `false` | `App.php` | **Implemented** |
| Timezone | `America/Chicago` | `App.php` | **Implemented** |
| Force HTTPS | `false` | `App.php` | **Implemented** |
| Session | File handler, cookie `ci_session`, 7200 seconds, path `writable/session`, match IP `false`, regenerate every 300 seconds, destroy-on-regenerate `false` | `App.php` | **Implemented** as config. Not started by `Home`. |
| Cookies | Prefix empty, domain empty, path `/`, secure `false`, HTTP only `false` | `App.php` | **Implemented** |
| Proxy IPs | empty string | `App.php` | **Implemented** |
| CSRF filter | Alias defined, commented out of `$globals['before']` | `Filters.php` | **Implemented** (off) |
| CSRF token names | Token `csrf_test_name`, header `X-CSRF-TOKEN`, cookie `csrf_cookie_name`, expire 7200, regenerate `true`, redirect `true` | `App.php` | **Implemented** as names. Unused while the filter is off. |
| Honeypot filter | Alias defined, commented out of globals. Config: hidden `true`, label `Fill This Field`, field name `honeypot` | `Filters.php`, `Honeypot.php` | **Implemented** (filter off) |
| Content Security Policy | `$CSPEnabled = false` | `App.php` | **Implemented** |
| Toolbar filter | On for every response (`after`) | `Filters.php` | **Implemented** |
| Query collector | Registered on `pre_system` only when `ENVIRONMENT !== 'production'` | `Events.php` | **Implemented** |
| Encryption key | empty string; driver `OpenSSL` | `Encryption.php` | **Implemented** |
| Cache | Handler `file`, backup `dummy`, path `writable/cache/` | `Cache.php` | **Implemented**. No application cache calls. |
| Logger | Threshold `3` (critical and above). Handler `CodeIgniter\Log\Handlers\FileHandler`. Path empty, so the framework default `writable/logs/` applies (comment in `Logger.php`). New log files mode `0644`. | `Logger.php` | **Implemented** |
| Exceptions | Logged, except status `404`. Error views in `app/Views/errors`. | `Exceptions.php` | **Implemented** |
| Migrations flag | `$enabled = true`. Table name `migrations`. No migration files in `app/Database/Migrations/`. | `Migrations.php` | **Implemented** |
| Modules discovery | Enabled, including Composer packages, for events, registrars, routes, services | `Modules.php` | **Implemented** |
| Validation | Rule sets are the four framework classes (Rules, FormatRules, FileRules, CreditCardRules). No application rule groups. | `Validation.php` | **Implemented** |
| Email | `fromEmail`, `fromName`, and `recipients` are unset. `userAgent` is `CodeIgniter`. No application code sends mail. | `Email.php` | **Implemented** as unused config |
| Development boot | `display_errors` on, `SHOW_DEBUG_BACKTRACE` true, `CI_DEBUG` 1 | `app/Config/Boot/development.php` | **Implemented** |
| Production boot | `display_errors` off, `CI_DEBUG` 0 | `app/Config/Boot/production.php` | **Implemented** |
| Default database | MySQLi, host `localhost`, port `3306`, username, password, and database empty, debug on when environment is not `production` | `Database.php` | **Implemented**. Unused. |

`CI_ENVIRONMENT` is commented out in `env`. Which boot file runs when `.env` is missing is decided in the framework package. **Unknown** for the unpinned `^4` install.

## Rules named in the README that the code does not enforce

Each line is **Documented** in `README.md` and absent from application logic. Do not treat them as current rules.

| README statement | What would have to exist | In the tree? |
|---|---|---|
| Login screen is password protected | A form, a check, a failure path | No |
| 2FA authentication | A second factor and a check | No |
| Dashboard menu: Dashboard, Profile, Documentation, Settings | Routes and a menu | No |
| Settings contain User Management and User group | Screens and stored groups | No |
| Weather widget | A widget and a data source | No |
| Profile shows the logged-in user, an uploaded image, and a security group | A user record, an upload, a group assignment | No |
| User management can add and delete users | A write path and a uniqueness or permission rule | No |
| User management can create roles or groups | A role store | No |
| MySQL is part of the stack | A schema and credentials | Driver name only |
| Bootstrap 4 is part of the stack | Front-end assets | No |

No validation message, password length, lockout, or role matrix is defined anywhere in `app/`.

The example session test sets `logged_in` to `123`. That is a PHPUnit sample in `tests/session/ExampleSessionTest.php`. It is not an application rule that a logged-in user has id 123.

## Permissions

There is no authentication and no authorisation check. **Implemented** absence: `app/Filters/` is empty and the filter list does not include an auth filter.

`public/robots.txt` is `User-agent: *` and `Disallow:` with an empty path, which allows crawlers. **Implemented**.

Who may deploy or change the repository is not written down. Git history shows one author, Jaco Brits. That is history, not a permission rule.

## Workflows

The only implemented workflow is: request `/` → receive `jaco`.

The README's login → dashboard → profile / user-management workflow is **Documented** and not implemented. Steps, inputs, and failure behaviour for it are **Unknown / Requires Confirmation** if that product is still wanted.

## Conflicts

- README says the login screen is password protected. `login.php` echoes a fixed string. Code and README disagree. The code is what runs. The README needs a human decision before it can be treated as a requirement.
- `contributing.md` requires GPG-signed commits to CodeIgniter's `develop` branch. The three commits on this repository are on `master` and the file does not show they were verified as signed. The contributing file is upstream CodeIgniter text, not an Overdrive rule for `skeleton`.
- README installation does not mention `composer install`. The app cannot boot without `vendor/`. The missing step is **Inferred**, not a business rule.
