Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a735a94
Add %env() config resolution + symfony/dotenv for Docker hosting
claude Aug 16, 2026
827850e
v7: remove committed environment-*.json variant dependency from gf
claude Aug 17, 2026
f3138cf
v7: unified root config.json; flatten config API; root-level overlays
claude Aug 17, 2026
b162aff
Add deprecated getAppConfig()/getEnvironmentConfig() pass-throughs
claude Aug 26, 2026
1f8f832
v7: replace overlay files with config.json environments section; hard…
claude Aug 26, 2026
aa2d2a0
set up mattpocock skills
andrewsauder Aug 26, 2026
e9c4ea2
Add CONTEXT.md: domain glossary for the framework
claude Aug 26, 2026
19e7eda
CONTEXT.md: add Zone, Ops Repo, Provisioning
claude Aug 26, 2026
67059c6
Record ADRs 0001-0004 for the v7 configuration and deployment design
claude Aug 26, 2026
c6a1adb
v7: require PHP >= 8.4
claude Aug 26, 2026
a9f404c
v7 phase 02: fail-closed config, secret files, stderr logging, health…
claude Aug 27, 2026
f6dfc78
Fold the Framework Services into the framework, activated from config…
claude Aug 27, 2026
f8bf21d
Migrate service tests, and teach gf migrate about services
claude Aug 27, 2026
d8d0955
Record ADR 0005 and update the docs for config-activated services
claude Aug 27, 2026
f1caa94
Fix the two PHPStan findings this change introduced
claude Aug 27, 2026
b88f0d8
Record ADRs 0006 and 0007 for certificate issuance and deployment-sec…
claude Aug 27, 2026
7ba5910
Rewrite ADR 0007: Azure Key Vault per Zone, not a GCP project
claude Aug 27, 2026
7c44e5b
Amend ADR 0003: Azure Key Vault, and /etc rather than /run for host s…
claude Aug 28, 2026
57ea4f8
Fix the v7 review findings: routing at domain root, lifecycle refusal…
claude Aug 28, 2026
72758f1
Close the remaining review findings: MFA request validation, docs sca…
claude Aug 28, 2026
e7a44cd
Enforce requiredRoles in the router, not in the optional auth service
claude Aug 28, 2026
58bc775
Merge remote-tracking branch 'origin/claude/v7-branch-review-s0nwyf' …
andrewsauder Aug 28, 2026
50ab8ab
Fix create-user fatal, route-override matching, and CLI bootstrap gaps
claude Aug 29, 2026
2b20b14
Lowercase JWT key GUIDs at generation
claude Aug 29, 2026
ea05124
Add gf user:create, and document running a v7 app locally
claude Aug 31, 2026
6c27804
Record the replica-set decision, and move the local-dev rules into th…
claude Aug 31, 2026
71b54c1
Fix four unit tests that only fail off Linux
claude Aug 31, 2026
4334e8c
Capture the log records tests provoke, instead of printing them
claude Aug 31, 2026
7633428
Say how real data reaches a development computer
claude Sep 3, 2026
4bb7284
docs(adr): move operational ADRs to gcgov/deploy and renumber 0001-0004
Sep 8, 2026
2dc4708
docs(context): widen the glossary for frontends and one operator
Sep 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php: ['8.3', '8.4']
php: ['8.4']
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
Expand All @@ -35,7 +35,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php: ['8.3', '8.4']
php: ['8.4']
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
Expand Down
324 changes: 232 additions & 92 deletions CLAUDE.md

Large diffs are not rendered by default.

187 changes: 187 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# gcgov/framework

The domain language of the framework itself and of the applications built on it. This file is a
glossary: it fixes what each term means so that code, documentation, and conversation use one word
per concept. It is not a specification — see `README.md`, `readme/`, and `docs/adr/` for those.

## Language

### Applications and extensions

**Application**:
A deployable unit that runs in exactly one Zone and is built from its own repository. It takes one
of two Application Kinds — an `api` built on the framework as a library, or a `frontend` that talks
to one.
_Avoid_: project, site, instance, consumer

**Application Kind**:
Which of the two shapes an Application takes: `api` or `frontend`. The Kind decides which images a
Release is made of, and whether the Application holds Secrets at all.
_Avoid_: type, flavour, variant, shape

**Framework Service**:
An optional part of the framework that contributes routes, controllers and an auth guard to an
Application when the Application enables it in the `services` section of its Unified Config. A
Framework Service ships inside the framework; it is not separately installable.
_Avoid_: plugin, module, extension, package

**Provider**:
One of the ways the authentication Framework Service can establish an identity — a full OAuth server,
or the exchange of a Microsoft token the front end already holds. Exactly one is selected, because
`provider` is a single key.
_Avoid_: driver, strategy, backend, adapter

**Scaffold**:
The one-time act of creating a new Application from the application template.
_Avoid_: setup, generate

**Bootstrap**:
Bringing a scaffolded Application to a runnable state — naming it, giving it the values its Config
References need, and generating the keys it signs with. Unlike a Scaffold it is idempotent: it is
re-run as an Application's configuration grows, and adds only what is missing.
_Avoid_: setup, init, provisioning, first-run

### Request handling

**Route**:
A binding of an HTTP method and URL pattern to a controller method, together with the
authentication and role requirements that reaching it implies.

**CLI Route**:
A Route dispatched from the command line rather than from an HTTP request. CLI Routes are never
authenticated.
_Avoid_: command, task, job

**Auth Guard**:
A router's authentication check, run before a Route with authentication enabled is dispatched. An
Application has its own; each Framework Service may add one.
_Avoid_: middleware, filter, interceptor

**Auth User**:
The authenticated identity for the current request, carrying its roles. Populated by an Auth Guard
and absent on unauthenticated Routes.
_Avoid_: current user, principal, session user

**Bootstrap User**:
The first user of an Application, created out of band because nothing else can create it. An
Application that admits only users already stored, and that gates user administration on a role,
can produce no first identity from the outside — the Bootstrap User is what breaks that circle.
_Avoid_: admin user, seed user, root account, initial user

**Controller Response**:
The value a controller method returns, describing what to send and how to serialize it. Returning
one is the only way a controller may end a request.
_Avoid_: result, output, payload

### Documents

**Model**:
A document that is stored as a collection in its own right and can be loaded, saved, and deleted
independently.
_Avoid_: entity, record, document class

**Embeddable**:
A document that exists only nested inside a Model or another Embeddable, and is never stored in a
collection of its own.
_Avoid_: sub-document, nested model, value object

**Embedded Copy**:
A duplicate of a Model's data stored inside other documents for read convenience, which the
framework refreshes wherever it appears whenever the original Model is saved.
_Avoid_: join, denormalization, reference, cache

**Typemap**:
The declaration of which class each part of a stored document hydrates into. Typed arrays require
an explicit element type; without one the array cannot be hydrated.

### Configuration and deployment

**Environment**:
A deployment target — local, production — distinguished *only* by the set of variables its
processes are given. Nothing is activated, copied, or selected by name; supplying a different
variable set is what makes an Environment different.
_Avoid_: variant, stage, tier, environment file, profile

**Unified Config**:
The single committed configuration file at an Application's root. It is Environment-invariant: the
same bytes are correct in every Environment.
_Avoid_: app config, environment config, config files, settings file

**Config Reference**:
A placeholder inside the Unified Config naming an environment variable, optionally through a chain
of processors. Every Config Reference is required — an unresolvable one is a startup failure, never
a silent fallback.
_Avoid_: token, placeholder, interpolation, variable expansion

**Secret**:
A configuration value that must never be committed and must never enter a process's environment —
credentials, connection strings, signing keys, API keys.
_Avoid_: credential, sensitive value, private setting

**Secret File**:
The file a Secret is delivered as at runtime, named by a Config Reference rather than carrying the
Secret's value in the environment itself.

**Zone**:
A network isolation boundary, defined by what it can reach and what can reach it. Three exist:
internal-only, public with internal access, and public without. An Application in production runs in
exactly one Zone; a Zone is not an Environment, and the two vary independently.
_Avoid_: server, host, network, tier, segment

**Ops Repo**:
The single private repository describing what runs on every host — encrypted Secrets, production
compose definitions, and the shared proxy stack — organized by Zone. An Application repository never
holds production topology or Secrets.
_Avoid_: infra repo, config repo

**Provisioning**:
Writing an Application's configuration onto a host — its decrypted Secrets, its compose file, and its
Zone's values. Performed by an operator as a step deliberately separate from deploying, so that no
host holds a decryption key and no deploy needs one.
_Avoid_: secret sync, secret deploy, key distribution

**Release**:
A tagged, immutable build of an Application, identified in production by a set of named content
digests — one per image the Application's compose file declares — rather than by tag or branch.
Deploying and rolling back are both the act of pointing a host at a different Release.
_Avoid_: version, build, deployment

**Zone Key Vault**:
The vault holding the single key that encrypts one Zone's Secrets, and nothing else. Three exist,
one per Zone, deliberately apart from the cloud project holding an Application's data-encryption
keys — those are reachable from a host and these must never be.
_Avoid_: KMS, key store, secrets vault, ops project

**Delegation Zone**:
A DNS zone holding only the ACME challenge records for one Zone, so that Zone's DNS credential can be
scoped to it rather than to a domain that serves traffic.
_Avoid_: ACME zone, challenge domain, validation domain

**Break-glass Key**:
The offline key every file in the Ops Repo is encrypted to in addition to its Zone's key, so that a
total loss of cloud access is still recoverable. Retrieving it obliges replacing it.
_Avoid_: recovery key, backup key, master key

**Escrow Custodian**:
The person who may retrieve the Break-glass Key from physical escrow. A second person can open the
same safe, so the key survives the custodian's absence; that second person is a control on
availability, not a witness to retrieval.
_Avoid_: key holder, key owner, keeper

### Retired language

These terms named real things in v6 and no longer name anything. They are listed so that they are
recognized as history rather than reintroduced.

- **App config / Environment config** — the two configuration files merged into the Unified Config.
- **Environment variant** — a named Environment whose connection details were committed. Removed
along with the ability to read another Environment's database from a workstation.
- **Scaffolding token** — a marker replaced once at Scaffold time. Replaced by Config References
and by generated developer environment files.
- **Service namespace registration** — the array of namespace strings an Application returned from
`\app\app::registerFrameworkServiceNamespaces()` to enable Framework Services. Replaced by the
`services` section of the Unified Config, so that enabling a service and configuring it are one
statement. The separately published `gcgov/framework-service-*` packages remain real, but only for
v6 Applications; the framework conflicts with them.
- **Auth plugin** — either of the two separate authentication packages. There is now one
authentication Framework Service with two Providers.
106 changes: 58 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ corresponding front end application.

Framework package requirements from `composer.json`:

* PHP `>=8.3`
* PHP `>=8.4`
* PHP extensions: `ext-mongodb`, `ext-fileinfo`, `ext-pdo`

Install dependencies with Composer:
Expand All @@ -30,17 +30,23 @@ composer install
The framework expects these app classes/files to exist in your `/app` directory:

* `\app\app` implementing `\gcgov\framework\interfaces\app`
* `\app\router` implementing `\gcgov\framework\interfaces\router`
* `\app\router` implementing `\gcgov\framework\interfaces\appRouter`
* `\app\renderer` implementing `\gcgov\framework\interfaces\render`

Controllers should implement `\gcgov\framework\interfaces\controller`.

Required configuration files:

* `/app/config/app.json`
* `/app/config/environment.json`
* `/config.json` — the unified configuration at the application root (merged app + environment sections,
secrets and per-environment values referenced via `%env(...)%`)

If either file is missing, the framework throws a config exception during request handling.
If it is missing, the framework throws a config exception during request handling.

config.json supports **Symfony-style `%env(...)%` environment-variable references**, so
secrets (Mongo URIs, client secrets, SMTP credentials) can be injected from the process
environment, Docker/Kubernetes secrets, or a `.env` file instead of being stored in the files.
Existing plain-JSON config keeps working unchanged. See
**[readme/environment-variables.md](readme/environment-variables.md)**.

## System Architecture

Expand All @@ -50,19 +56,14 @@ All apps utilizing the framework for an entire lifecycle should use this file st

```
/api
├── config.json
├── app
│ ├── app.php
│ ├── constants.php
│ ├── renderer.php
│ ├── router.php
│ ├── cli
│ │ ├── index.php
│ │ ├── local.bat
│ │ ├── local-debug.bat
│ │ └── prod.bat
│ ├── config
│ │ ├── app.json
│ │ └── environment.json
│ │ └── index.php
│ ├── controllers
│ │ └── {controller.php}
│ └── models
Expand All @@ -77,22 +78,16 @@ automatically start with some extra folders and tools.
```
/api
│...
├── config.json # committed unified config; secrets/per-env values via %env(...)
├── .env.example # copy to .env (gitignored); holds gf db:*/env PROD_* vars too
├── www
│ │...
│ ├── web.config
│ ├── web-local.config
│ └── web-prod.config
├── app
│ │...
│ └── config
│ └── environment-local.json
│ └── environment-prod.json
├── scripts
│ ├── create-jwt-keys.ps1
│ └── setup.ps1
├── docker
│ └── nginx
│ └── default.conf.template
├── srv
│ ├── {env}
│ │ └── php.ini
│ ├── tmp
│ │ ├── files
│ │ ├── opcache
Expand All @@ -101,11 +96,10 @@ automatically start with some extra folders and tools.
│ │ └── tmp
│ └── jwtCertificates
├── db
│ ├── backup
│ ├── restore-live-to-local.ps1
│ └── local-createuser.js
├── logs
└── update-production.ps1
├── Dockerfile
└── docker-compose.yml
```

### Core Files and Application Namespacing
Expand Down Expand Up @@ -219,22 +213,30 @@ gf cli /structure/cleanup # run a CLI route (replaces app/cli/{env}.bat)
gf cli /structure/cleanup --debug# run with Xdebug (replaces local-debug.bat)
gf cli:list # list the app's CLI routes
gf cert:generate-auth # JWT signing keys (replaces create-jwt-keys.ps1)
gf db:restore --from=prod # pull a source env's mongo dbs into the local env
gf db:run db/script.js # run a mongosh script with config-managed credentials
gf env local # activate environment config file variants
gf setup # bootstrap a scaffolded app (replaces setup.ps1)
gf deploy # tag-based deployment (replaces update-production.ps1)
gf env # validate config.json resolves against this environment
gf env --list # every variable config.json references, and which are set
gf env --init # write/extend the .env skeleton from config.json
gf init --title="My API" # bootstrap a scaffolded app (replaces setup.ps1)
gf migrate # convert a v6 application to the v7 layout
gf user:create --email=… --roles=… # create the account you sign in as (the first user)
```

Removed in v7: `deploy` (a Release is an immutable image pinned by digest — see ADR 0002),
`db:restore` (it required production credentials on every workstation), and `setup`
(replaced by the non-interactive `init`).

Tab completion is available for bash/zsh/fish (`gf completion --help`) and PowerShell
(`gf completion:powershell`), including dynamic completion of the app's CLI route names.
Apps and plugins can add their own gf commands via a `cli\commandProvider` class.

**See [readme/gf.md](readme/gf.md) for the full reference and the migration guide.**
**See [readme/gf.md](readme/gf.md) for the full reference and the migration guide**, and
[readme/local-development.md](readme/local-development.md) for what an application needs in order to
run on a development computer.

The legacy per-app entry (`> app/cli/{env}.bat {url-path}`, `local-debug.bat` for XDebug) keeps
working, but new apps should use gf. The `scripts/*.ps1` files shipped with the framework are
deprecated in favor of `gf setup` and `gf cert:generate-auth`.
deprecated in favor of `gf init` and `gf cert:generate-auth`.

## Framework Services

Expand Down Expand Up @@ -340,7 +342,7 @@ For full reference, configuration options, attributes, and detailed examples, se


### PDODB
Initiate PDO connections using SQL connection details in app/config/environment.json. It is only a small wrapper around
Initiate PDO connections using SQL connection details in config.json (`sqlDatabases`). It is only a small wrapper around
the native PDO class.

Read user connection: `new gcgov\framework\services\pdodb\pdodb(true, $databaseName)`
Expand All @@ -351,18 +353,26 @@ Write user connection: `new gcgov\framework\services\pdodb\pdodb(false, $databas
## Extensions
Extensions add service or app level functionality to the app that registers them. Extensions may expose new endpoints.

* **Open API Documentation** `gcgov/framework-service-documentation`
* https://github.com/gcgov/framework-service-documentation
* Add namespace `\gcgov\framework\services\documentation` to `\app\app->registerFrameworkServiceNamespaces()`
* **Microsoft Auth Token Exchange** `gcgov/framework-service-auth-ms`
* https://github.com/gcgov/framework-service-auth-ms-front
* Add namespace `\gcgov\framework\services\authmsfront` to `\app\app->registerFrameworkServiceNamespaces()`
* **Oauth Server Service** `gcgov/framework-service-auth-oauth-server`
* https://github.com/gcgov/framework-service-auth-oauth-server
* Add namespace `\gcgov\framework\services\authoauth` to `\app\app->registerFrameworkServiceNamespaces()`
* **User CRUD** `gcgov/framework-service-user-crud`
* https://github.com/gcgov/framework-service-user-crud
* Add namespace `\gcgov\framework\services\usercrud` to `\app\app->registerFrameworkServiceNamespaces()`
* **Cron Monitor** `gcgov/framework-service-gcgov-cron-monitor`
* https://github.com/gcgov/framework-service-gcgov-cron-monitor/
* Add namespace `gcgov\framework\services\cronMonitor` to `\app\app->registerFrameworkServiceNamespaces()`
Framework Services ship inside the framework. Enable one by adding its block to the `services`
section of `config.json` — presence enables it, and the block's contents are its settings.

```jsonc
"services": {
"auth": { "provider": "oauth" }, // or "msFront"
"userCrud": { },
"documentation": { }
}
```

* **Authentication** `services.auth` — one service, two providers.
* `provider: "oauth"` — full OAuth server: password, third-party and authorization-code grants, MFA.
* `provider: "msFront"` — exchange a Microsoft token the front end already holds for an app JWT.
* Either way you get `/.well-known/jwks.json`, `/auth/fileToken`, and a JWT guard over every
`authentication: true` route.
* **User CRUD** `services.userCrud` — `/user` CRUD over the resolved user model.
* **Open API Documentation** `services.documentation` — `GET /documentation.yaml`.
* **Cron Monitor** — not a Framework Service; construct
`\gcgov\framework\services\cronMonitor\cronMonitor` directly and set `cronMonitor.url`.

The separately published `gcgov/framework-service-*` packages remain available for **v6** applications.
The framework conflicts with them, so a v7 application cannot install both.
Loading
Loading