Skip to content
Back to skills

Dotkernel Project Variants

ASecurity

Use at the start of any non-trivial task in a Dotkernel repository, and whenever a convention might differ between projects — to work out whether this is Dotkernel API, Admin, Frontend, Light, Queue or a derived project, and what that implies for handler naming, authorization config, responses, forms, templates, documentation and tests. Also use when a skill's guidance does not match what the repo actually contains, or when the same feature has to be built in more than one Dotkernel project.

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
securitygophpbashapidatabasefrontenddocumentation

Works with

  • cli
  • api

Security analysis

A100/100

Scanned November 7, 2026

npx -y skills add dotkernel/dotboost --skill dotkernel-project-variants --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Dotkernel Project Variants?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Dotkernel Project Variants
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/dotkernel-dotkernel-project-variants/badge)](https://www.skillsdirectory.com/skills/dotkernel-dotkernel-project-variants)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: dotkernel-project-variants
description: >
  Use at the start of any non-trivial task in a Dotkernel repository, and whenever a convention
  might differ between projects — to work out whether this is Dotkernel API, Admin,
  Frontend, Light, Queue or a derived project, and what that implies for handler naming,
  authorization config, responses, forms, templates, documentation and tests. Also use when a
  skill's guidance does not match what the repo actually contains, or when the same feature has
  to be built in more than one Dotkernel project.
---

# Which project am I in, and what changes?

```text
Applies to
  Projects:  - all
  Packages:  - none
```

Detect first, then apply. Guessing produces confidently wrong code that looks plausible.

The tables below describe the stock repositories' default branches as of 2026-10-30: API `7.0`,
Admin `7.0`, Frontend `5.0`, Light `1.0`, Queue `2.0`. Other branches and derived projects can
differ — the repo in front of you wins.

## Detection

Read `composer.json`:

```bash
grep -m1 '"name"' composer.json
grep -A 12 '"psr-4"' composer.json
grep -E 'mezzio-hal|laminas-form|dot-controller|dot-session|dot-rbac-guard|oauth2|swagger-php|dot-cli|messenger' composer.json
```

Then confirm with the filesystem:

```bash
ls src                                   # module names, and whether a Core layer exists
ls src/*/templates 2>/dev/null           # templates present → templated project
ls src/*/src/Controller 2>/dev/null      # controllers present → Frontend-style
ls config/autoload | grep -i authorization
ls src/*/src/OpenAPI.php 2>/dev/null     # documented API
```

| Variant | Root namespace | Distinctive packages | Distinctive files |
| --- | --- | --- | --- |
| **API** | `Api\` + `Core\` | `mezzio/mezzio-hal`, `mezzio-authentication-oauth2`, `zircote/swagger-php`, `mezzio/mezzio-cors` | `src/Core/`, `Collection/`, `OpenAPI.php`, `authorization.global.php`, `documentation/command/` |
| **Admin** | `Admin\` + `Core\` | `dot-session`, `dot-rbac-guard`, `dot-twigrenderer`, `dot-flashmessenger` | `src/Core/`, `templates/`, `Form/`, `authorization.global.php` + `authorization-guards.global.php` |
| **Frontend** | `Frontend\` | `dot-controller`, `laminas/laminas-form`, `dot-session`, `dot-rbac-guard` | `Controller/`, `templates/`, `Form/`, `Entity/` inside modules, `data/doctrine/migrations/` |
| **Light** | `Light\` | `mezzio-twigrenderer` and little else — no Doctrine, no session, no forms | `Handler/`, `templates/`, hand-written `Factory/` classes |
| **Queue** | `Queue\` (flat, `src/`) | `dot-cli`, `netglue/laminas-messenger`, `symfony/redis-messenger` | `src/Swoole/Command/`, `src/App/Message/`, no HTTP routes |

A project derived from one of these keeps its parent's conventions. Infer the parent from the
PSR-4 roots and installed packages.

## What actually differs

| Concern | API | Admin | Frontend | Light | Queue |
| --- | --- | --- | --- | --- | --- |
| Request entry point | handler, `{Verb}{Resource}[{Sub}][{Action}]{Resource\|Collection}Handler` | handler, `{Verb}{Action}{Resource}[Form]Handler` — `GetCreateAdminFormHandler`, `PostCreateAdminHandler` | controller with an `{action}` route segment — `UserController` | handler — `GetPageViewHandler` | console command |
| Update verb | `PATCH` | `POST` (form submit) | `POST` (form submit) | n/a | n/a |
| Authorization | RBAC roles → route-name permissions, `authorization.global.php` | roles → permissions in `authorization.global.php`; route name → permission in `authorization-guards.global.php` | same two files; guards keyed by route **and action** | none | none |
| Unlisted route | denied | **allowed** — guards use `POLICY_ALLOW` | **allowed** — guards use `POLICY_ALLOW` | n/a — every route is public | n/a |
| Input | `laminas-inputfilter` | `laminas-form` (wrapping an InputFilter) + CSRF | `laminas-form` + CSRF | none | message payloads |
| Response | HAL resource/collection, ProblemDetails errors | rendered template, redirect, flash message | rendered template, redirect, flash message | rendered template | exit codes, logs |
| Documentation | per-module `OpenAPI.php`, `documentation/command/*.md` | none | none | none | none |
| Sessions/CSRF | none — stateless tokens | required | required | none | n/a |
| Entities | `Core` layer | `Core` layer | inside each module | none | none |
| Migrations | `src/Core/src/App/src/Migration/` | `src/Core/src/App/src/Migration/` | `data/doctrine/migrations/` | none | none |
| DI | `#[Inject]` + `AttributedServiceFactory` | `#[Inject]` + `AttributedServiceFactory` | `#[Inject]` + `AttributedServiceFactory` | hand-written factories | `#[Inject]` + `AttributedServiceFactory` |
| Scaffolding | `dot-maker` | `dot-maker` | none | none | none |
| Tests | `test/Unit/`, `test/Functional/` | `test/Unit/`, `test/Functional/` | `test/Unit/` | `test/Unit/` | `test/App/`, `test/Swoole/` |

The `documentation/` directory is the least reliable row: derived projects add or drop it freely,
so check for it before writing docs there (`dotkernel-feature-docs`) rather than inferring it.

## What is the same everywhere

- Mezzio underneath, with per-module `ConfigProvider`s registered in `config/config.php`.
- Laminas Coding Standard, phpstan, phpunit; `composer check`. Twig-rendering projects add
  `composer twig-cs-check`.
- Evolution over versioning for anything clients depend on.
- Secrets only in `*.local.php`.
- UTF-8, LF, `declare(strict_types=1)`.

## Working across two projects at once

In the stock repositories only API and Admin share a `Core` layer — both carry the same `Core\`
modules over one database. A feature spanning those two means: the entity, repository and shared
service go in `Core` **once**; each project gets its own handlers, routes, authorization entries
and presentation. Change a `Core` signature and you have changed it for every project on that
database — check the others before you do, and mention it (see `dotkernel-core-submodule`).

Frontend keeps its own entities and migrations, so a feature spanning Frontend and API means two
models of the same data. Say so rather than assuming a shared `Core`.

## Rule of thumb

Before writing a file, open the two closest existing files of the same kind **in this repo** and
match them. That beats every table above, because it reflects the version you are actually on.
When the repo contradicts a skill, follow the repo and tell the user the skill needs updating.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.