# Technical documentation — skeleton

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

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

No technical document existed in the repository or in `silicon_ark/skeleton/` before this file. `contributing.md` and `tests/README.md` are upstream CodeIgniter 4 texts. `README.md` is the Overdrive readme. This file records the tree that was inspected. It does not add a target architecture.

## Summary

`skeleton` is the CodeIgniter 4 application starter (`composer.json` name `codeigniter4/appstarter`) with `Home::index` pointed at `app/Views/login/login.php`. The framework code is not in git. `app/Config/Paths.php` loads it from `vendor/codeigniter4/framework/system` after Composer install.

The README calls the project Silicon Skeleton and lists an admin product (login, 2FA, dashboard, profile, users). That product is not in the source. See `prd.md` and `features.md`.

## Languages and frameworks

| Item | Evidence | Provenance |
|---|---|---|
| PHP `>= 7.2` | `composer.json`; `public/index.php` dies below 7.2; README "Server Requirements" | **Documented** and **Implemented** |
| CodeIgniter 4, package `codeigniter4/framework` `^4` | `composer.json` | **Implemented** as a constraint. Exact version **Unknown** (`composer.lock` gitignored, `vendor/` absent) |
| PHPUnit `8.5.*` | `composer.json` `require-dev`. `tests/README.md` mentions 8.5.2 as the version at the time that file was written | **Implemented** constraint. **Documented** in `tests/README.md` |
| `mikey179/vfsstream` `1.6.*` | `composer.json` `require-dev` | **Implemented** |
| Bootstrap 4 | README link only | **Documented**. No files. |
| MySQLi | `app/Config/Database.php` `$default['DBDriver']` | **Implemented** as config |
| SQLite3 | `$tests['DBDriver']` and `phpunit.xml.dist` | **Implemented** for the test group |

PHP extensions named in the README: `intl`, `libcurl` (only if HTTP CURLRequest is used), `json`, `mbstring`, `mysqlnd`, `xml`. **Documented**. Application code under `app/Controllers` and `app/Views` does not reference those extensions directly.

`app/Common.php` is the starter stub for overriding procedural functions. It defines no functions.

## Repository layout

```text
app/                 application code and config (HTTP denied)
  Config/            framework and app configuration
  Controllers/       BaseController.php, Home.php
  Database/          Migrations/.gitkeep, Seeds/.gitkeep
  Filters/           .gitkeep
  Helpers/           .gitkeep
  Language/          .gitkeep
  Libraries/         .gitkeep
  Models/            .gitkeep
  ThirdParty/        .gitkeep
  Views/             login/login.php, welcome_message.php, errors/
public/              HTTP front controller, .htaccess, favicon.ico, robots.txt
tests/               PHPUnit examples and tests/_support fixtures
writable/            cache, logs, session, uploads (HTTP denied)
builds               PHP script, not a directory
composer.json
contributing.md      upstream CodeIgniter 4 contributing guide
env                  environment template (copy to .env; .env is gitignored)
LICENSE
license.txt
phpunit.xml.dist
README.md
spark                CLI entry
```

143 tracked files at `43b5796`. `vendor/` is not one of them.

Top-level `builds` is a file (`#!/usr/bin/env php`), not a build-output directory. PHPUnit coverage logs are configured to write to `build/logs/` (`phpunit.xml.dist`). That output directory is not committed. `.gitignore` ignores `build/` only as a NetBeans-related pattern in the IDE section (`build/` with no leading slash, so any `build` directory is ignored).

## Request lifecycle

### HTTP

1. `public/index.php` compares `phpversion()` to `7.2`.
2. `FCPATH` is the `public` directory. Working directory is changed there.
3. `app/Config/Paths.php` supplies system, app, writable, tests, and view directories.
4. `require` of `{systemDirectory}/bootstrap.php` returns the app object.
5. `$app->run()`.

System directory value: `__DIR__ . '/../../vendor/codeigniter4/framework/system'` from `app/Config/Paths.php`. **Implemented**.

`app/Config/Constants.php` defines `APP_NAMESPACE` as `App` and `COMPOSER_PATH` as `ROOTPATH . 'vendor/autoload.php'`.

### CLI

1. `spark` exits on SAPI `cgi`.
2. `SPARKED` is defined. `FCPATH` is `public/`.
3. Same paths and bootstrap.
4. `CodeIgniter\CLI\Console` runs the command. Status code `>= 300` becomes the process exit code. Errors are displayed (`error_reporting(-1)`, `display_errors` 1) for the CLI process.

### Routing

`app/Config/Routes.php`:

- Loads `SYSTEMPATH . 'Config/Routes.php'` when that file exists (inside the framework package).
- Default namespace `App\Controllers`, controller `Home`, method `index`.
- `setAutoRoute(true)`.
- Explicit route: `$routes->get('/', 'Home::index')`.
- If `app/Config/{ENVIRONMENT}/Routes.php` exists it is loaded afterwards. No environment route directory is in the commit.

`Home::index` returns `view('login/login')`. **Implemented**.

`BaseController::initController` calls the parent and does not load helpers, models, or the session. The helpers array is empty.

## Environments

Boot files:

| File | Behaviour |
|---|---|
| `app/Config/Boot/development.php` | `error_reporting(-1)`, `display_errors` 1, `SHOW_DEBUG_BACKTRACE` true, `CI_DEBUG` 1 |
| `app/Config/Boot/production.php` | `display_errors` 0, `error_reporting` excludes notices, deprecations, and strict, `CI_DEBUG` 0 |
| `app/Config/Boot/testing.php` | Same display settings as development: `display_errors` on, `SHOW_DEBUG_BACKTRACE` true, `CI_DEBUG` 1. Selected when the environment is `testing`. |

Which file runs depends on `CI_ENVIRONMENT`. In the committed template `env`, the line `# CI_ENVIRONMENT = production` is commented. No `.env` is in git. The fallback when the variable is unset is inside `vendor/`, which is not committed. **Unknown** for constraint `^4`.

`Events.php` flushes output buffers on `pre_system` when `ENVIRONMENT !== 'testing'`, then starts a pass-through buffer. When `ENVIRONMENT !== 'production'` it registers the debug toolbar's database collector.

## Configuration reference

Values that change behaviour are listed in `business-rules.md`. Technical notes that did not fit there:

- `app/Config/App.php` `$baseURL` is `http://localhost:8080/`. PHPUnit overrides `app.baseURL` to `http://example.com` inside the test process (`phpunit.xml.dist`).
- `app/Config/Encryption.php` `$key` is empty. The encryption library cannot be used until a key is set. Nothing in `app/` constructs the encrypter.
- `app/Config/Migrations.php` enables migrations and sets the history table to `migrations`. Timestamp format for `php spark migrate:create` is in that file. No application migration exists to run.
- `app/Config/Modules.php` discovers events, registrars, routes, and services in the app and in Composer packages.
- `app/Config/Validation.php` has no `$login` or other rule group. The templates are the framework list and single views.
- `app/Config/ContentSecurityPolicy.php` exists. `App::$CSPEnabled` is `false`, so that policy is not applied by the flag in `App.php`.
- `env` documents commented keys for environment, base URL, session, cookies, CSRF, CSP, database, and honeypot. All of those lines are comments. Copying `env` to `.env` without editing does not override `App.php` or `Database.php`.

Commented database examples in `env` use host `localhost`, database `ci4`, username `root`, password `root`, driver `MySQLi`. They are comments in the upstream template. `Database.php` itself uses empty username, password, and database name.

## Data

No application tables.

`app/Database/Migrations/.gitkeep` and `app/Database/Seeds/.gitkeep` are the only files. There is no user, role, group, session, or profile schema.

`tests/_support/Database/Migrations/` contains example migrations used by the framework test support classes, including `20160428212500_Create_test_tables.php` and `2020-02-22-222222_example_migration.php`. Those are not application migrations. `tests/_support/Models/UserModel.php` is a test double, not an `App\Models` class.

Default connection (`app/Config/Database.php`):

- Driver `MySQLi`, host `localhost`, port `3306`, database empty, user empty, password empty.
- Charset `utf8`, collation `utf8_general_ci`.
- `DBDebug` true when `ENVIRONMENT !== 'production'`.
- Persistent connect false, cache off, encrypt false, compress false, strict mode false, prefix empty.

Test connection: SQLite3 memory database, prefix `db_`.

Charset `utf8` (3-byte) rather than `utf8mb4` is the committed value. Whether that was a choice or the starter default is **Inferred** as the starter default. It was not discussed in the README.

## Auth

No authentication library, filter, or user table.

Session config exists (file handler, `writable/session`) and is unused by `Home`. CSRF filter is not in the global before list. Encryption key is empty. Cookie secure and HTTP-only flags are `false`. HTTPS is not forced.

`public/.htaccess` forwards an `Authorization` header. No application code reads it.

## Background work, queues, storage, monitoring

- No queue, cron, worker, or scheduler file.
- No Docker, no `bitbucket-pipelines.yml`, no cloud template.
- File storage paths: `writable/cache`, `writable/logs`, `writable/session`, `writable/uploads`. Only `index.html` placeholders are committed. Upload handling is not implemented.
- Logging: framework file handler, threshold 3, directory `writable/logs` when `Logger::$path` is empty. **Implemented** as config.
- Debug toolbar filter is on for all responses (`Filters.php`).
- `sonar-project.properties` and `sonarqube_scan.bat` are not in `43b5796`. A 2026-09-28 directory listing named them. **Unknown / Requires Confirmation**.
- `public/robots.txt` allows all crawlers.

## Security notes from the committed tree

These are observations, not a security review.

- `app/` and `writable/` are denied in Apache `.htaccess`. The front controller is `public/index.php`.
- `public/.htaccess` turns directory indexes off (`Options All -Indexes`) and sets `ServerSignature Off`.
- CSRF and honeypot filters are present and not enabled.
- `cookieHTTPOnly` and `cookieSecure` are `false`. `forceGlobalSecureRequests` is `false`. The `www` redirect, which runs only when HTTPS is off, hard-codes `http://`.
- Auto-routing is on.
- `.env` is gitignored. The template's sample database password is inside a comment.
- `composer.lock` is gitignored, so installs are not pinned.
- Dependencies are not vendored. Supply of `codeigniter4/framework` `^4` is whatever Composer resolves on the day of install. The `^4` range includes many later 4.x releases. The repo has not moved since February 2020. What `composer install` selects now is **Unknown** until someone generates a lock file.

## Development and tests

Documented local run (`README.md`):

1. `git clone https://jacooverdrive@bitbucket.org/overdrive/skeleton.git`
2. The following lines in the README are `git add`, `git commit`, `git pull`, `git push`. They are not required to run the app.
3. Copy `env` to `.env` and set base URL and database settings.
4. `php spark serve`, which the README says launches `http://localhost:8080`.

**Inferred** prerequisite, absent from the README: `composer install` so that `vendor/codeigniter4/framework` and `vendor/autoload.php` exist. `spark` and `public/index.php` both require the framework bootstrap.

Tests (`tests/README.md`, upstream text):

- `composer install`
- Optional symlink `vendor/bin/phpunit` to `./phpunit` on OS X or Linux. `.gitignore` ignores a file named `phpunit`.
- Xdebug for coverage.
- `./phpunit` from the project root, or `composer test` (`composer.json` script `"test": "phpunit"`).
- Coverage example writes `tests/coverage.txt` and `tests/coverage/`. `phpunit.xml.dist` also logs clover, HTML, testdox, and JUnit under `build/logs/`.

`tests/README.md` tells the reader to copy `phpunit.dist.xml`. The committed file is `phpunit.xml.dist`. The name in that sentence does not match the file. **Documented** versus **Implemented** filename mismatch.

Database tests use the in-memory SQLite group unless reconfigured. `tests/README.md` also describes editing `app/Config/Database.php` or `phpunit.xml` for a live database and excluding `@DatabaseLive`. No `@DatabaseLive` test is in the application tests that were read (`HealthTest`, `ExampleDatabaseTest`, `ExampleSessionTest`).

`HealthTest::testBaseUrlHasBeenSet` passes if `.env` contains a line matching `app.baseURL = '.` or if `ConfigReader->baseUrl` is non-empty. `App::$baseURL` is non-empty, so the test can pass without a `.env`.

This onboarding did not run `composer install` or PHPUnit. Whether the suite passes on a current PHP is **Unknown**. PHP 7.2 is past upstream support. A modern PHP may refuse PHPUnit 8.5 or this CodeIgniter 4 line. **Unknown / Requires Confirmation**.

## Dependency switch script

`builds` usage: `php builds [release|development]`.

| Argument | What it writes |
|---|---|
| `release` | Removes `minimum-stability` and the GitHub VCS repository if present. Sets require `codeigniter4/framework` to `^4@rc`. Replaces path strings `vendor/codeigniter4/codeigniter4` with `vendor/codeigniter4/framework` in `Paths.php`, `phpunit.xml.dist`, and `phpunit.xml` when those files exist. |
| `development` | Sets `minimum-stability` to `dev`, adds VCS repo `https://github.com/codeigniter4/codeigniter4`, requires `codeigniter4/codeigniter4` `dev-develop`, removes `codeigniter4/framework`. Rewrites the same path strings the other way. |

The script prints the files it changed and tells the operator to run `composer update`. It is not a build pipeline. Committed `composer.json` does not match a fresh `release` run, because the require is `^4` rather than `^4@rc`.

## Deployment

No deployment config, host name, or process manager is in the repository. `baseURL` is localhost. How this app is hosted, if it is hosted, is **Unknown / Requires Confirmation**.

`public/` is the intended document root (**Inferred** from `index.php` living there and from `app/` being denied). Confirm with whoever runs the server.

## Related names

`silicon_ark/unity-skeleton/` is a different repository (a Fuse React app). It is not this CodeIgniter starter. Do not merge their requirements.
