Search by

drupal / mutamad_project

Drupal-Infrastructure

Project template for the Mutamad distribution.

Package info

git.drupalcode.org/project/mutamad_project.git

Homepage

Issues

Type:project

pkg:composer/drupal/mutamad_project

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

1.0.x-dev 2026-11-15 12:04 UTC

This package is auto-updated.

Last update: 2026-11-15 09:05:33 UTC


README

Mutamad Project — المشروع

Mutamad project template

What an adopter create-projects. It pins Drupal core and the Mutamad profile, scaffolds a settings fragment, and ships the DDEV environment.

Built and maintained by iSpectra — Imagine. Implement. Inspire. See iSpectra on drupal.org.

composer create-project drupal/mutamad_project my-site
cd my-site
ddev start
ddev install-mutamad

ddev install-mutamad installs in Arabic; ddev install-mutamad --lang=en installs in English. Either way the site has both languages, each behind its URL prefix (/ar, /en), and the language it was installed in is the default before the first field exists — which matters: Drupal fixes a field's language when the field is created.

What install-mutamad does

It runs the installer — drush site:install mutamad with the site template — and nothing else. The profile and its recipes hold everything the site needs, including what recipes cannot do: making settings.php load settings.mutamad.php, creating the private files directory outside the web root, and generating the MFA encryption key.

Then it runs mutamad-verify on the result, and exits with its status.

Checking the controls

ddev mutamad-verify

Checks every setting a Mutamad control rests on, on the running site, and fails if any has drifted. Each check is tagged with the control it evidences (NCA ECC/DCC, DGA). It reads behaviour, not just settings: the cookie a real login receives, the headers a real response carries, whether a private file can be downloaded anonymously.

CI runs the same checks on a fresh install (mutamad-verify job), plus a WCAG 2 AA scan of the Arabic and English pages (mutamad-accessibility, not yet blocking).

Hardened settings

assets/settings.mutamad.php and assets/services.mutamad.yml are scaffolded into web/sites/default/:

SettingControl
Private files in private-files/, beside web/ — outside the web root, so no web server rule is needed to protect themECC 3-2-3
Session cookie ends when the browser closes; a session unused for 12 hours is discarded; strict session IDsECC 2-2-4
update.php cannot be run anonymouslyECC 3-2-2
No X-Powered-By headerECC 3-2-1
Refuses to run on PHP older than 8.3ECC 10-2
Every log entry to the database log and to syslog as one JSON line — facility LOCAL0, ident mutamad — through Monolog (mutamad_logging). The host must run a syslog daemon that forwards LOCAL0 to the SIEMECC 12-2-1

Multi-factor authentication

Administrators, site administrators and content approvers must set up an authenticator app (mutamad_crypto). Each gets 3 logins to do it, then is refused until it is done — including webmaster. composer.json allows drupal/tfa's alpha on purpose: TFA 2 has no stable Drupal 11 release, and only the root composer.json can allow one. It allows drupal/canvas_translate's alpha for the same reason: Canvas Pages are translated with it (mutamad_canvas). And drupal/entityqueue_form_order's: editors add a question, a person or a life journey to its queue from the edit form with it (mutamad_faq, mutamad_people, mutamad_life_journeys).

Libraries served by the site

The Content Security Policy loads scripts and styles from the site only, so a library a module would fetch from a CDN must be under web/libraries instead — which also suits a closed network. composer.json installs two there, each from a package repository in the form its module's README gives, since neither is on Packagist:

LibraryPackageUsed by
Tagifyyaireo/tagifyThe tags widget (News's Tags)
Dropzoneenyo/dropzone, the official releaseMedia bulk upload (mutamad_media)

Only the root composer.json can declare a repository, which is why the recipes that need these libraries do not require them. ddev mutamad-verify fails if either library is missing.

Storybook

The Mutamad theme's components, each rendered by this site, with its settings as controls, in English and Arabic, and an accessibility check per story. For development sites only.

ddev storybook init    # the Storybook module, its permission, CORS; the stories' JSON
ddev storybook start   # https://<site>.ddev.site:6006
ddev storybook gen     # after changing a component's stories
ddev storybook build   # a static Storybook in storybook-static/
ddev storybook disable # turn the site's side off again

init copies assets/services.storybook.yml into web/sites/default/: CORS for the Storybook app and the module's development mode. settings.mutamad.php loads it under DDEV only (IS_DDEV_PROJECT), so neither reaches a site that faces the public. The app's settings are in .storybook/, its packages in package.json, and its port in .ddev/config.storybook.yaml. For a theme generated from the Mutamad theme, set STORYBOOK_THEME to its folder.

Before this site faces the public

  1. Set $settings['trusted_host_patterns'] in web/sites/default/settings.mutamad.php. Without it Drupal answers to any Host header, which makes password-reset poisoning trivial.
  2. Read the hash salt from a file outside the repository, and turn on read-only configuration in production — both are shown, commented, in settings.mutamad.php.
  3. On nginx, let browsers cache the static files, fonts included. Apache needs nothing: Drupal's .htaccess caches every file for a year. nginx follows its own configuration, and one with no rule for fonts sends the theme's IBM Plex Sans Arabic with none, so a browser checks it again on every page (Lighthouse: "Serve static assets with an efficient cache policy"). In the site's server block:

    location ~* \.(css|js|woff2?|ttf|otf|eot|jpe?g|gif|png|webp|svg|ico)$ {
        try_files $uri @rewrite;
        expires max;
        log_not_found off;
    }
    

    Drupal's aggregated CSS and JavaScript change address when they change, so they can be cached for good too. The development site (DDEV) caches fonts the same way (.ddev/nginx/fonts.conf), and leaves CSS and JavaScript uncached while the theme is being worked on.

  4. Send email through the entity's mail relay: set MUTAMAD_MAILER_DSN in the web server's environment, for example smtp://user:password@relay.example.gov.sa:587. Without it, Drupal sends by the server's own sendmail, and on a server with none every email — the workflow's notifications, password resets — is lost without an error. The site's email address must be on the entity's domain, covered by its SPF, DKIM and DMARC records. See settings.mutamad.php.
  5. Run cron every minute, from the server: Mutamad turns core's Automated Cron off (mutamad_logging), so nothing runs it otherwise — and without cron the site stops checking for security updates, expiring passwords and trimming its log, with no error. In the crontab of the user that owns the site:

    * * * * * cd /var/www/example.gov.sa && vendor/bin/drush cron --quiet
    

    DDEV does it already (.ddev/config.cron.yaml).

Licence

GPL-2.0-or-later.

Maintainers

iSpectra

Built and maintained by iSpectra (on drupal.org).