Architecture overview

How the application is put together, for a developer who has to extend or audit it.

No framework, on purpose. The product runs on shared hosting where a framework's console tooling, queue workers and build steps are not available. What it uses instead is a small router, plain PHP templates, and PDO. There is nothing to compile and nothing to install: the dependencies ship in vendor/.

A request, end to end

  1. public/index.php is the only entry point. Everything else is rewritten to it.
  2. bootstrap/app.php loads the autoloader and .env, starts the session with its security attributes, applies the security headers, checks the host against ALLOWED_HOSTS, and dispatches.
  3. routes/web.php maps the path to a controller method and states the middleware it requires: auth, a role, or a permission.
  4. The controller validates input, calls a repository or a service, and renders a view with a layout.
  5. Repositories own the SQL. Services own the work that is not a database read or write.

The layout of the code

PathWhat lives there
routes/web.phpEvery address, with its middleware. The one file to read to see the whole surface.
app/Controllers/HTTP actions, grouped by area: public, auth, admin, verifier, profile, install.
app/Repositories/PDO access. Prepared statements only, and the place authorisation-sensitive record selection happens.
app/Services/Import, export, email, audit, QR, PDF rendering, branding, translation, SMTP settings, requirements checking, installation.
app/Security/Sessions and authentication, CSRF, role and permission checks, TOTP, password policy, tokens, rate limiting, headers.
app/Validation/The validator and its rules.
resources/views/Plain PHP templates and layouts. Output is escaped with e().
lang/One file per locale, holding every visible string as a flat key.
public/assets/Vanilla CSS and JavaScript, and the bundled fonts. No build step, no bundler.
database/schema.sql and the role and permission seed.
bin/migrate.php, seed.php, create-admin.php.
cron/The scheduled jobs.

Classes are autoloaded as App\ from app/, so a new class needs no registration. Composer's autoloader is used when vendor/ is present, with a small fallback for the rest.

Where the data model turns

Extending it

Adding a screen

  1. Add the route in routes/web.php with the middleware it needs. Never rely on a hidden link for protection.
  2. Add the controller method, validate input with the validator, and keep SQL in a repository.
  3. Add the view under resources/views/ and render it with the matching layout: layouts/admin, layouts/public or layouts/system.
  4. Add every new visible string as a key in both locale files. The two files must hold the same keys.
  5. Write an audit entry for anything that changes data, with the changed field names and safe before and after values.

Adding a permission

Add it to database/seeds/001_roles_permissions.sql, grant it to the roles that should hold it, and require it on the route. Re-run php bin/seed.php. Per-account grants are deliberately unsupported: the seed empties that table.

Adding a language

Copy lang/en.php to lang/<code>.php, translate the values, add the code to available_locales in config/app.php, and check that the bundled fonts cover the script. The two shipped locales are complete, so a missing key is a mistake rather than a fallback.

Changing the certificate rendering

Layouts are stored as normalised coordinates plus per-field typography, and rendered to PDF with the bundled renderer. Because each document stores its own render snapshot, a change to the renderer affects new documents and regenerations, not documents already stored. Test with a regeneration before assuming otherwise.

Two things to leave alone. The document snapshot mechanism preserves approved business values and name-correction history. The public route exposes certificate details and PDFs only for verified, non-revoked records; an unarchived revoked record has only a status notice. Keep both boundaries intact when extending the product.

Licensing and dependencies

THIRD-PARTY-LICENSES.md in the package root lists every bundled component and its licence, and the full licence texts ship with it. Some are covered by the LGPL, which is why the licence you bought explicitly permits modifying the product for your own use and reverse engineering those modifications. Do not strip, minify or patch anything inside vendor/ in a copy you redistribute internally: it must stay complete and unmodified.