Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
66 changes: 66 additions & 0 deletions docs/book/v7/architecture-at-a-glance.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Architecture at a Glance

## Summary

A single-page map of how Dotkernel API is put together: the Core/App split introduced in 6.0, the Headless Platform and modular-monolith layout, the path a request takes through the middleware pipeline into a handler, the roles of handlers, services, repositories, input filters and entities, how configuration and dependency injection are organized, the PSRs behind it all, and the four security layers.

## Details

Dotkernel API follows a modular, middleware-based architecture designed for scalability and maintainability.
Understanding the core structure is essential before diving into development.

Expand Down Expand Up @@ -226,3 +232,63 @@ They ensure that your code can integrate with other PSR-compliant libraries.
| Handler | Request/response mapping | Extract user ID, call service |
| Service | Business rules | Validate user data, calculate totals |
| Repository | Data queries | Find users, save entity |

## FAQ

**Q: Where should I put a new feature?**

A: In the App layer, under `src/App/src/` or your own module.
Core (`src/Core/src/`) is reserved for infrastructure such as authentication, database access and shared entities.
See [Core and App](extended-features/core-and-app.md).

**Q: What is the execution order from request to database?**

A: `Handler → Service → Repository → Database`.
The handler maps the request, the service holds business rules, and the repository is the only layer that touches the database.

**Q: Which middlewares run before my handler?**

A: CORS, authentication, authorization, content negotiation and routing, in that order, as defined in `config/pipeline.php`.
See [Middleware flow](flow/middleware-flow.md).

**Q: What runs after the handler returns?**

A: The response-side middlewares: problem details for exceptions, the deprecation headers, and any custom response headers.

**Q: What are the built-in modules?**

A: `Admin`, `User`, `Security`, `App` and `Core`.
Your own modules — `Book`, `Product`, `Article` and so on — follow the same pattern.

**Q: Is this a monolith or microservices?**

A: Out of the box it is a modular monolith, structured so that individual modules can later be split into separate services.

**Q: Where is business logic supposed to live?**

A: In services, not handlers or repositories.
Handlers extract and validate request data and delegate; repositories only query and persist.

**Q: How do dependencies reach my classes?**

A: Through constructor injection declared with the `#[Inject]` attribute and resolved by `AttributedServiceFactory`.
See [Dependency injection](core-features/dependency-injection.md).

**Q: Which configuration file does what?**

A: `config.php` is the entry point, `pipeline.php` the middleware stack, `container.php` the DI container, and `config/autoload/` holds per-concern files — with `local.php` for environment-specific values that stay out of version control.

**Q: What are the four security layers?**

A: Authentication with OAuth2 tokens, authorization with RBAC permissions, input validation with input filters, and content negotiation on `Accept` and `Content-Type`.
See [Basic security](security/basic-security.md).

**Q: Which databases are supported?**

A: MariaDB and PostgreSQL.
See [Server requirements](introduction/server-requirements.md).

**Q: Which PSRs are core rather than supporting?**

A: PSR-7, PSR-11 and PSR-15 are core to the architecture; the rest arrive through dependencies.
See [PSRs](introduction/psr.md).
33 changes: 33 additions & 0 deletions docs/book/v7/commands/create-admin-account.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Creating admin accounts in Dotkernel API

## Summary

The `admin:create-admin` CLI command creates an administrator account from the command line, taking an identity, a password and a first and last name.
Accounts created this way always receive the `admin` role.

## Usage

Run the following command in your application’s root directory:
Expand Down Expand Up @@ -35,3 +40,31 @@ You can get more help with this command by running:
```shell
php ./bin/cli.php help admin:create
```

## FAQ

**Q: Can I choose the role of the created account?**

A: No. The command always assigns the `admin` role; other roles must be set afterwards.
See [Authorization](../core-features/authorization.md).

**Q: What can I use as the identity?**

A: Either a username or an email address, as long as it is not already taken.

**Q: My name or password contains special characters and the command fails. What do I do?**

A: Surround the value in double quotes so the shell passes it through unchanged.

**Q: Are the short and long option forms equivalent?**

A: Yes.
`-i`, `-p`, `-f` and `-l` are shorthand for `--identity`, `--password`, `--firstName` and `--lastName`.

**Q: How do I know the account was created?**

A: The command prints `Admin account has been created.` and the account is immediately usable.

**Q: Where do I see the full command help?**

A: Run `php ./bin/cli.php help admin:create`.
36 changes: 36 additions & 0 deletions docs/book/v7/commands/display-available-endpoints.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Displaying Dotkernel API endpoints using dot-cli

## Summary

The `route:list` CLI command inspects the application's routes at runtime and prints every endpoint's request method, route name and path.
Results can be filtered by name, path or method.

## Usage

Run the following command in your application’s root directory:
Expand Down Expand Up @@ -71,3 +76,34 @@ Get more help by running this command:
```shell
php ./bin/cli.php route:list --help
```

## FAQ

**Q: Is the output generated from a static file?**

A: No. The command walks the application's registered routes in realtime, so it always reflects the current configuration.

**Q: Which filters are available?**

A: `-i|--name`, `-p|--path` and `-m|--method`.
They are case-insensitive and can be combined.

**Q: Why do route names matter beyond documentation?**

A: Because a permission in Dotkernel API is a route name, so this listing is also the list of permissions you can grant.
See [Authorization](../core-features/authorization.md).

**Q: My new route does not appear. What should I check?**

A: That its module's `RoutesDelegator` is registered and the route is declared there.
See [Route grouping](../extended-features/route-grouping.md).

**Q: How is this different from the OpenAPI documentation?**

A: `route:list` reports what the application actually routes; the OpenAPI file describes the documented contract.
Comparing the two is a quick way to spot undocumented endpoints.
See [OpenAPI documentation](../openapi/introduction.md).

**Q: Where do I see the full command help?**

A: Run `php ./bin/cli.php route:list --help`.
38 changes: 38 additions & 0 deletions docs/book/v7/commands/generate-database-migrations.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Generate a database migration without dropping custom tables

## Summary

`doctrine-migrations diff` generates migrations from your entity mappings, but it also emits `DROP TABLE` statements for unmapped tables such as `oauth_*`.
Passing a `filter-expression` excludes those prefixes so the generated migration leaves them alone.

## Usage

Run the following command in your application’s root directory:
Expand Down Expand Up @@ -62,3 +67,36 @@ You can get more help with this command by running:
```shell
vendor/bin/doctrine-migrations help diff
```

## FAQ

**Q: Why does the generated migration try to drop my `oauth_*` tables?**

A: Because no Doctrine entity describes them.
From the ORM's point of view they are not part of the schema, so `diff` proposes removing them.

**Q: What should I do with a migration that already contains those DROP queries?**

A: Delete that migration file and regenerate it with a `filter-expression`, rather than editing the queries out by hand.

**Q: Why do the quotes differ between platforms?**

A: Windows shells require double quotes around the expression, while Linux and macOS shells require single quotes to prevent the pattern from being interpreted.

**Q: How do I exclude more than one prefix?**

A: Concatenate the prefixes with a pipe inside the negative lookahead, for example `/^(?!foo_|bar_)/`.

**Q: The filter is ignored in PowerShell. What is happening?**

A: PowerShell treats `^` as a special character and strips it, so the expression arrives without the anchor.
Escaping does not help — run the command from your IDE, a Linux shell, or the Command Prompt instead.

**Q: Where do generated migrations end up?**

A: Under `data/doctrine/migrations/`.
See [Doctrine ORM](../installation/doctrine-orm.md).

**Q: How do I see all options for the command?**

A: Run `vendor/bin/doctrine-migrations help diff`.
37 changes: 37 additions & 0 deletions docs/book/v7/commands/generate-tokens.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Generating tokens in Dotkernel API

## Summary

`token:generate` is a multipurpose CLI command that issues the tokens different parts of the API require.
Currently it supports the `error-reporting` type, whose generated value is pasted into `config/autoload/error-handling.global.php`.

## Details

This is a multipurpose command that allows creating tokens required by different parts of the API.

## Usage
Expand Down Expand Up @@ -62,3 +69,33 @@ Save and close `config/autoload/error-handling.global.php`.
```shell
php ./bin/clear-config-cache.php
```

## FAQ

**Q: Which token types can the command generate?**

A: `error-reporting`.
Run `php ./bin/cli.php token:generate --help` to see the current list.

**Q: What is the error reporting token for?**

A: It authorizes calls to the error reporting endpoint, so only clients holding the token can submit error reports.
See [Error reporting](../core-features/error-reporting.md).

**Q: Where do I put the generated token?**

A: In the `tokens` array under `ErrorReportServiceInterface::class` in `config/autoload/error-handling.global.php`.

**Q: Can I configure more than one token?**

A: Yes.
`tokens` is an array, so several valid tokens can coexist — useful when rotating a token without downtime.

**Q: The token has no effect after I saved the config. Why?**

A: Outside development mode the configuration is cached.
Clear it with `php ./bin/clear-config-cache.php`.

**Q: Does the command store the token for me?**

A: No. It only prints the value; copying it into the configuration file is a manual step.
67 changes: 67 additions & 0 deletions docs/book/v7/core-features/authentication.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Authentication

## Summary

Dotkernel API authenticates with the OAuth2 password grant through `mezzio/mezzio-authentication-oauth2`.
Clients exchange credentials at `POST /security/generate-token` for an access token and a refresh token, then send the access token in the `Authorization` header; `POST /security/refresh-token` renews it.
Requests without an `Authorization` header get a default `guest` identity, which can reach only public endpoints.

## Details

Authentication is the process by which an identity is presented to the application.
It ensures that the entity making the request has the proper credentials to access the API.

Expand Down Expand Up @@ -185,3 +193,62 @@ Get new Access Token ───────────────────
- **Rotate credentials**: Change default OAuth client secrets in production.
- **Token expiration**: Access tokens expire (default 1 day). Implement refresh logic in clients.
- **Never expose refresh tokens**: Refresh tokens should only be stored client-side, never in logs or public code.

## FAQ

**Q: What happens if a request sends no `Authorization` header?**

A: The application assigns a default `guest` identity, an instance of `Mezzio\Authentication\UserInterface`.
Guests can reach public endpoints but not protected ones.

**Q: Which OAuth2 grant does the API use?**

A: The password grant: credentials are exchanged once for tokens, and subsequent requests carry the access token instead of the credentials.

**Q: What must I run before authenticating for the first time?**

A: The migrations and fixtures — `php ./vendor/bin/doctrine-migrations migrate` followed by `php ./bin/doctrine fixtures:execute` — which create the OAuth tables and seed the initial credentials.
See [Doctrine ORM](../installation/doctrine-orm.md).

**Q: What are the seeded credentials?**

A: `admin` / `dotadmin` for the admin account and `test@dotkernel.com` / `dotkernel` for the user account.
Remove or change them before production.
See [Basic security](../security/basic-security.md).

**Q: Why are admins and users in separate tables?**

A: To keep application users away from data that only administrators should reach.
Authenticated identities therefore come from either the `admin` or the `user` table.

**Q: Which parameters does the token request need?**

A: `grant_type`, `client_id`, `client_secret`, `scope`, `username` and `password`.
The client values come from `oauth_clients` and the scope from `oauth_scopes`.

**Q: How long do the tokens last?**

A: Access tokens expire after one day and refresh tokens after one month, both configurable under the `authentication` key in `config/autoload/local.php`.

**Q: How do I renew an expired access token?**

A: Post the `refresh_token` to `/security/refresh-token` with `grant_type` set to `refresh_token`.
If the refresh token has expired too, authenticate again with credentials.

**Q: I get an "Invalid scope" error. What is wrong?**

A: `scope` must be `"api"` — it is the only configured scope.

**Q: I get "Invalid credentials" even though the password is right. What else could it be?**

A: A mismatched `client_id` or `client_secret` against the `oauth_clients` table, or an account that does not exist or is inactive.

**Q: Which middleware performs authentication?**

A: `Api\App\Middleware\AuthenticationMiddleware`, which runs before authorization in the pipeline.
See [Middleware flow](../flow/middleware-flow.md).

**Q: Is authenticating enough to access an endpoint?**

A: No. Authentication establishes the identity; the role still needs permission for the route.
See [Authorization](authorization.md).
46 changes: 46 additions & 0 deletions docs/book/v7/core-features/authorization.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Authorization

## Summary

Authorization decides whether an already-authenticated identity may reach a given resource.
Dotkernel API implements it with role-based access control through `Mezzio\Authorization\Rbac\LaminasRbac`, applied by `AuthorizationMiddleware` and configured in `config/autoload/authorization.global.php`, where each permission is a route name and roles inherit from their parents.

## Details

Authorization is the process by which a system takes a validated identity and checks if that identity has access to a given resource.

**Dotkernel API**'s implementation of authorization uses `Mezzio\Authorization\Rbac\LaminasRbac` as a model of Role-Based Access Control (RBAC).
Expand Down Expand Up @@ -70,3 +77,42 @@ A permission in Dotkernel API is basically a route name.
As you can see, the `superuser` does not have its own permissions, because it gains all the permissions from `admin`, no need to define explicit permissions.

The `user` role, gains all the permission from `guest` so no need to define that `user` can access `home` route, but `guest` cannot access user-specific routes.

## FAQ

**Q: How does authorization differ from authentication?**

A: Authentication establishes who the caller is; authorization checks what that established identity is allowed to do.
See [Authentication](authentication.md).

**Q: What exactly is a permission in Dotkernel API?**

A: A route name.
Granting a role a permission means granting it access to the route of that name.

**Q: Where do I add permissions for a route I just created?**

A: To the relevant role's array in `config/autoload/authorization.global.php`.
A route with no permission entry is unreachable for that role.

**Q: Which access control model is used?**

A: RBAC, via `mezzio-authorization-rbac` backed by `laminas-permissions-rbac`.

**Q: How does role inheritance work here?**

A: A role listed inside another role's entry is its parent's beneficiary: because `admin` lists `superuser`, `superuser` receives everything granted to `admin`.
That is why `superuser` needs no explicit permissions of its own.

**Q: Where are roles stored?**

A: Each authenticatable entity — admin or user — has its own `roles` table where its roles are defined.

**Q: Which middleware enforces this?**

A: `Api\App\Middleware\AuthorizationMiddleware`.
See [Middleware flow](../flow/middleware-flow.md).

**Q: Can I use ACL instead of RBAC?**

A: The ACL adapter ships with the project, but RBAC is what Dotkernel API is configured for; switching means replacing the authorization configuration.
Loading