Skip to content

Liber Quartus · Tome I

How grim is assembled

A running site is six kinds of thing put together, and only one of them, the smallest, is written for that site alone.

≈8 min grim 1.0.0
Source

You run one command in an almost empty directory and an application appears: a Laravel skeleton, dozens of modules, two themes, a Docker stack. Where did each part come from, who wrote it, and which of them would you change if something had to change? The parts are few. Most confusion about grim comes from taking an artefact for a source, and editing the copy instead of the thing it was copied from.

Six pieces, six homes

Every name below comes from one rule. Everything lives in the grimoiry organization on GitHub, and every repository starts with grim-.

Piece What it is Where it lives Describes itself in
grim-core The application skeleton and the core modules grimoiry/grim-core core.json
A module One feature: a provider, routes, migrations, views grimoiry/grim-<name>, such as grim-cart module.json
A theme The look of one of two slots, frontend or admin grimoiry/grim-theme-<slug>, grimoiry/grim-theme-admin-<slug> theme.json
A project One client, one application A repository of its own, and the image ghcr.io/grimoiry/grim-<project> grim.json, grim.lock
The CLI The tool that assembles the rest grimoiry/grim-cli, shipped as one PHAR Its own version
The registry The portal at grim.grimoiry.com A web application, not a package Nothing on your disk

The first three are packages. A project is a list of packages. The last two are neither: they are what reads the list and what remembers things no list can hold. The fields of the five files are the subject of Project and package manifests.

Packages are released, not written

Nobody works in grimoiry/grim-cart. Core, modules and themes are developed and released together from one source, and the package repositories are what a release leaves behind: a tree and a tag. That is why they share a version line, which Release trains explains, and why a change to a module never starts in its package repository.

Piece Who changes it How the change travels
grim-core The platform maintainers A new train
A shared module or theme Anyone, from inside a project grim push turns your edit into a pull request against the source, and a release brings it back as a patch
A module or theme of your own You grim make:module and grim make:theme scaffold it in the project and can give it a repository
A project The team of that client A commit to grim.json and grim.lock
The CLI The platform maintainers A release of the PHAR, fetched by grim self-update

core.json is the clearest case of an artefact. It carries the version of core, the PHP it needs and the list of core modules, and grim reads it to tell a core module from one you may require or remove. Nobody edits it. It is generated at release and lands in the project with the rest of core.

A project is the only piece written for one site

A project is two files. grim.json names the core train, the modules, the themes for each slot, the PHP version and the environments. grim.lock records what those names resolved to. Everything else in the directory is fetched or rendered from them, which is the argument of What grim owns in your project.

Two more things belong to a project and are not in its repository. The first is its image, built by grim build and named ghcr.io/grimoiry/grim-<project> unless grim.json names another owner or a whole image. The second is what each environment keeps for itself: a .env, a database, uploaded files.

Artefact Made from Made by
A package tag The shared source A release
grim.lock grim.json and the tags that exist grim install, grim require, grim update
The project directory grim.lock and the templates in the CLI grim install
The image The project directory grim build
A running environment The image, and that server's .env and data grim deploy

Read the table downwards and each row is the input of the next. Nothing in it flows back up, so a fix belongs in the highest row where the fault exists.

The CLI carries the infrastructure, not the application

The CLI holds no application code. It holds the resolver that turns constraints into a lock, the installers, and the templates of everything around the application: the Dockerfiles, the compose file, the nginx configuration, the stack a server runs, the scaffold of a new module or theme.

This is why it has a version line of its own. A new train changes what the application is. A new CLI changes how it is built and served, and grim upgrade is the moment a project takes that in. What the CLI keeps about you is outside any project, in ~/.grim: your GitHub token, your settings, the servers you have registered, a cached catalogue.

Where the pieces meet on disk

Assembly puts each package in a fixed place. The place is derived from the name, and nothing in it is configurable.

Path Holds Put there by
app/, bootstrap/, config/, routes/, core.json The skeleton A copy of the core release
src/<Name>/ One module per directory, core and required alike. cart becomes src/Cart/, namespace Modules\Cart Core modules with the copy, the rest as a git checkout at a tag
resources/themes/frontend/<slug>/, resources/themes/admin/<slug>/ Every theme of each slot A git checkout at a tag
config/modules-available.php The provider class of every required module, read from its module.json Generated on install and update
storage/config/modules-enabled.php The modules that are switched on Created once as a copy of the list above, then only added to
docker/, docker-compose.yml The stack Rendered from the templates in the CLI

Core modules and required modules sit side by side under src/ and look the same. The difference is in who lists them. Core modules are named by core itself and cannot be switched off. Required modules are named by your grim.json.

Where they meet at runtime

grim is absent at runtime. Once the files are in place the application is an ordinary Laravel application, and it puts itself together from three lists.

Question Answered by
Which core modules load? The list that ships inside core. Always all of them.
Which other modules load? storage/config/modules-enabled.php, or config/modules-available.php when that file does not exist
Which theme renders? THEME_FRONTEND and THEME_ADMIN in .env. A slot in grim.json may install several themes. The first one is written into a fresh .env, and after that .env decides.

Each provider registers the routes, migrations and views of its module. That is the whole mechanism. A module that is installed but not enabled is files on disk and nothing more.

Installing a module and enabling it are separate acts. grim require enables the modules you named and not the ones they pulled in. A dependency is on disk, locked and listed as available, and stays off until its line is in the enabled list. Add, trace and remove modules shows the line.

The registry holds what a repository cannot

The catalogue is not kept in the registry. grim search and grim list:available read the repositories of the organization on GitHub, with the token from grim auth:login, and cache the answer in ~/.grim for a day. What you can see there is what your account has been given access to. The same token is what the registry accepts, so there is no second login.

The CLI talks to the registry about When
Shared secrets, credentials every project needs and no repository should hold grim secrets:set stores one. grim install writes them into the local .env, and says nothing when it cannot.
A heartbeat: project, environment, modules, active themes, core version, PHP, host and URL After each successful grim release. A failed heartbeat never fails a release.
Error reports from the CLI When a command crashes, unless you have turned telemetry off
Access to a private package Never by itself. A refused install prints the address where you ask.

A project can point at another registry with registry_url in grim.json. Without the registry, install, update, build and deploy all still work. You lose the shared secrets and the overview of what runs where.

Questions

Is grim-core a module?

No. It is versioned like one and installed differently. A module is a checkout you can edit and send back. Core is copied over the project, and the core modules inside it arrive with the copy.

Can a project hold code that is in no package?

Yes. A module scaffolded under src/ is part of the project until you give it a repository. Track it in the project repository as What grim owns in your project describes, and the recipe Create a module covers the rest.

Where does a new project come from?

From the CLI alone. grim create-project writes a grim.json for the train of the day, and the first install does everything in the tables above. The recipe Create a project walks through it.