Local setup

TL;DR

The installation checklist is in README.md. This page is what surrounds it: what the host needs, what to re-run after a change, and the four things that look broken and are not.

The installation checklist lives in README.md, Parts 0 to 3, and stays there. This page does not restate it.

What the host needs

The container carries the pinned runtime, so the host does not need PHP 7.4 itself. What it needs is Docker and the ocean1-infra repository, whose startup.sh brings up the web server and the ocean1-ccc container together.

Nothing is installed with Composer. Smarty is vendored in the repository, and there is no build step.

Run PHP through the container rather than on the host, in the same form the crontab uses:

docker exec ocean1-ccc php /var/www/app/jobs/<group>/<file>.php

The application is at http://ccc.localhost/. It answers with no Cloudflare Access in front because AUTHORIZATION_DEFAULTJWT supplies an identity when the cookie is empty.

After a change

Re-run the installer whenever you add a page or change reference data:

docker exec ocean1-ccc php /var/www/app/jobs/install/install.php

It regenerates the permission matrix by scanning app/http/ on disk, so a new page returns 401 until it has run. On acceptance and production the deploy runs it; you run it yourself only locally and on test.

Never run it twice at once. Two concurrent runs duplicate the permission rows.

Four things that look broken and are not

A brand new environment does nothing on schedule. Every crons row is seeded active = 0 by design, so nothing runs until someone switches it on. That is why every job in the README's Part 1 list is run with force.

A job prints "Cron is paused" and stops. That is the switch at /system/crons, not a failure. Add force after the filename for a single run.

A page loads and is unreachable from the menu. The nav is hand-maintained. A page without an entry in templates/components/header.tpl works at its URL and appears nowhere, and nothing warns you.

A query returns nothing and no error appears. query() swallows mysqli errors and there is no logger. The usual causes are a typo'd column, a reserved word used as an alias, or a double-quoted string being read as an identifier because the server runs with ANSI_QUOTES. See the table in AGENTS.md, What fails silently.

The SQL mode matters and is not in this repository

The server runs with ANSI, ANSI_QUOTES, STRICT_ALL_TABLES, ONLY_FULL_GROUP_BY and PIPES_AS_CONCAT, and code depends on it. It is set on the server, not in this repository and not in .env, so a rebuilt local database can come up without it and nothing here would say so. The symptoms are a WHERE clause matching nothing or everything, and a column check against SHOW CREATE TABLE matching nothing on every table.

Check it before spending long on a query that behaves impossibly:

docker exec ocean1-ccc php -r 'define("DIR_ROOT","/var/www/"); define("DIR_CONFIG", DIR_ROOT."config/"); require_once(DIR_ROOT."app/models/database.php"); $d = New database(); $r = $d->query("SELECT @@sql_mode AS sqlmode"); $row = $r->fetch_assoc(); print($row["sqlmode"].PHP_EOL);'

It goes through the application's own connection, so it reports the mode the application sees rather than the mode a separate client negotiated.

Evidence

Prerequisites and the container form from README.md, Installation, and AGENTS.md, Commands. The installer's disk scan and the 401 symptom from AGENTS.md, Authorization. The concurrent-run warning from AGENTS.md, Development flow step 8. Switched-off default and the force flag from AGENTS.md, Jobs and the batches queue. The SQL mode list and its symptoms from AGENTS.md, Models and database, and What fails silently. Basis: code-checked at 30f5313.

About this document
  • Type
    development
  • Status
    active
  • Updated
    2026-09-14
  • Created
    2026-09-13