Skip to content

Configuration reference

The published config/docuccino.php drives everything. Every key is listed in the file itself — required keys active, optional ones commented out — so you can discover the whole surface by scrolling through it. This page is the long-form version: what each key does, what it defaults to, and where its behavior is explained in full. The file is plain data — no imports, no class references — so it stays safe to load even where Docuccino itself isn’t installed.

return [
'enabled' => env('DOCUCCINO_ENABLED', true),
'documents' => [ /* … */ ],
'extensions' => [],
'lint' => [ /* … */ ],
'engine' => [ /* … */ ],
'on_route_error' => 'skeleton',
'cache' => [ /* … */ ],
];
Key Default Effect
enabled env('DOCUCCINO_ENABLED', true) Master switch. When false, every command except docuccino:clear aborts with a notice and exits non-zero, and the runtime viewer endpoints (/docs/*) are not registered at all. Lets you disable generation and serving in an environment without removing config.
on_route_error 'skeleton' Per-route failure behavior. skeleton emits a stub operation plus an error diagnostic (never a dead build); omit drops the route entirely.

documents is a map of independent pipeline runs. Each entry has its own route filters, info, servers, security, content, and export target, and shares route contexts + the TypeEngine in-process. The published config ships one document, default.

'info' => [
'title' => 'API Documentation',
'version' => '1.0.0',
// 'description' => ['file' => 'resources/docs/api/description.md'],
],

Maps to OAS info, and any other OAS info field you add (contact, license, termsOfService, …) is emitted as written. description may be a Markdown string or ['file' => '…md'] to load your API’s introduction from a file. version is the value the versioning policy evaluates during docuccino:diff --enforce.

'servers' => [
['url' => 'https://api.example.com'],
// Server variables with defaults/descriptions:
// ['url' => 'https://{tenant}.example.com', 'variables' => [
// 'tenant' => ['default' => 'acme', 'description' => 'Tenant slug'],
// ]],
],

Emitted verbatim as OAS servers, including server variables. For a worked multitenant subdomain example ({tenant}.example.com), see Deploying to production.

'routes' => [
'include' => ['api/*'],
'exclude' => [],
'closure' => null, // fn (RouteDescriptor $route): bool => ...
'include_vendor' => false,
],

Route selection. include/exclude are URI globs; closure is an optional predicate that runs after the globs for arbitrary logic. A route must pass includes, fail excludes, and satisfy the closure to be documented.

Routes whose resolved controller class file lives under the application’s vendor/ directory are excluded by default — the same as php artisan route:list --except-vendor — so an installed package’s own routes don’t leak into your API reference. Closures and your own app controllers are never affected, and the include/exclude/closure filters are unchanged. Set include_vendor to true to document installed packages’ routes.

'security' => [
'auto_detect_middleware' => 'auth*',
// 'schemes' => [
// 'bearer' => ['type' => 'http', 'scheme' => 'bearer', 'bearerFormat' => 'JWT'],
// 'apiKey' => ['type' => 'apiKey', 'in' => 'header', 'name' => 'X-API-Key'],
// 'oauth2' => ['type' => 'oauth2', 'flows' => [...]],
// 'oidc' => ['type' => 'openIdConnect', 'openIdConnectUrl' => 'https://…'],
// ],
// 'default' => [['bearer' => []]], // per-op requirement for auth-detected routes
// 'document' => [['bearer' => []]], // document-wide security requirement
],
Key Default Effect
auto_detect_middleware 'auth*' Wildcard matched against each route’s middleware; a match applies the default requirement.
schemes none components.securitySchemes — full breadth: http bearer/basic, apiKey (header/query/cookie), oauth2 flow builders, OpenID Connect.
default none The per-operation security requirement applied to auth-detected routes.
document none A document-wide security requirement.

Declaring any schemes here defers the auto-config security integrations (Sanctum, Passport) — explicit config wins. #[Unauthenticated] clears a route’s requirement regardless.

'error_responses' => 'default', // 'default' | 'problem-details' | 'none'
// Or a bag, to also choose the Problem Details 422 `errors` shape:
'error_responses' => ['preset' => 'problem-details', 'errors_shape' => 'pointer-list'],

Selects the error-response strategy. default documents Laravel’s stock JSON error shapes; problem-details activates the RFC 9457 preset (application/problem+json); none emits no error responses. In every case, an inferred exception handler that recovers your app’s real error shape wins ahead of this fallback. The strategy also governs the implicit 401/422/404/403 responses (none of them are emitted under none).

errors_shape (only meaningful with the Problem Details preset) chooses how the 422 body models errors: map (a field-keyed map of message lists, the default) or pointer-list (a list of {detail, pointer} JSON-Pointer objects).

'tags' => [
'default_strategy' => 'controller', // 'controller' | 'none'
'map' => [],
// 'mapper' => Custom::class, // container-resolved TagMapper; default PrefixTagMapper over `map`.
// 'definitions' => [ // OAS top-level `tags`, sorted by weight then name:
// ['name' => 'Billing', 'summary' => 'Billing', 'kind' => 'nav', 'weight' => 0],
// ['name' => 'Forms', 'description' => '…', 'parent' => 'Billing'],
// ],
],

default_strategy tags an operation that has no #[Group]: controller (the default — the controller’s short name with a trailing Controller stripped, e.g. FormControllerForm, then run through map) or none (leave it untagged). Closure routes are never auto-tagged. map is a raw-tag → display-tag table (exact match wins, else the first matching prefix). mapper swaps in a custom TagMapper. definitions supplies OAS top-level tag objects.

A definition carries the full OAS 3.2 Tag Object: name (required), plus optional summary, description, parent and kind. weight is Docuccino’s own — it orders the emitted array (ascending weight, then name) and is never emitted.

Field Purpose
summary A short display label, where description is the prose.
parent The name of the tag this one nests under, for a grouped sidebar.
kind A machine-readable category — nav, badge, audience are the common ones; any string is legal.

parent must name another definition, and the links must not form a cycle. A parent naming an undefined tag emits a config.unknown-tag-parent info diagnostic; a link that closes a cycle emits config.tag-parent-cycle. Either way the offending link alone is dropped and the build carries on, so the emitted hierarchy is always a tree. Because the array is sorted before the parents are resolved, the result never depends on the order you wrote the definitions in.

summary, parent and kind are OpenAPI 3.2 only. Exporting 3.1 drops them, each with its own downlevel.tag-* warning — the tags themselves stay, flattened.

'content' => [
'dir' => null, // e.g. 'resources/docs/api'
],

Points at a markdown tree compiled into x-docuccino.content (pages + a compiled nav tree). Folders become default nav groups; frontmatter (title/slug/summary/tags + nav.{group,order,hidden,type,ref}) overrides. ::operation{...} / ::schema{...} directives are resolved against the document; broken refs become diagnostics. null compiles nothing. See Guides, pages & prose for the full workflow, or the UIR content layer for how it lives in the raw document.

'overlays' => [
// 'resources/docs/overlays/*.yaml',
],

Globs of OpenAPI Overlay 1.0 documents applied at assembly time as the overlay(45) precedence layer — a standards-based hand-edit layer that survives regeneration. See Customizing the output for worked examples.

'representation' => [
'filters' => 'bracketed', // bracketed | deepObject (Query Builder filter/field style)
'lists' => 'comma', // comma | array (Query Builder sort/include list style)
'nullable' => 'type-array', // type-array (type: [x, null]) | anyof ({type: null} branch)
'operation_id' => 'route-name', // route-name | controller-method ({ShortController}@{method})
// 'enums' => [
// 'naming' => 'none', // none | x-enumNames | x-enum-varnames
// 'components' => true, // true (hoist each enum to a $ref'd component) | false (inline everywhere)
// ],
],

Separates what was inferred from how it is expressed in the spec. The semantic facts stay stable in x-docuccino regardless of policy, so the diff engine can tell “representation changed” from “API changed”.

Key Values Default Effect
filters bracketed | deepObject bracketed Query Builder filter/field style: one flat filter[status] / fields[type] parameter each (bracketed), or a single filter / fields object parameter with style: deepObject (deepObject). Read at RepresentationPolicy, drives the deepObject/bracketed rendering.
lists comma | array comma Query Builder sort / include list style: a single comma-separated string (comma), or an exploded array parameter with style: form, explode: false and an items enum (array).
nullable type-array | anyof type-array How nullability is expressed: type: ["string","null"] vs a {type: null} anyOf branch (legacy tooling).
operation_id route-name | controller-method route-name operationId strategy.
enums.naming none | x-enumNames | x-enum-varnames none Codegen name hints on enum schemas (off by default); read by the Enum integration.
enums.components true | false true Whether each reflectable enum hoists to a shared #/components/schemas entry that properties and query-parameter item schemas $ref (true), or its type/enum/x-enumDescriptions are inlined at every use site (false).

One bag per integration, keyed by the integration’s config name; each integration reads only its own bag, and all are optional.

'integrations' => [
'api_resources' => ['wrap' => true], // top-level resource `data` wrapping
'sanctum' => ['modes' => ['token', 'stateful'], 'cookie' => 'myapp_session'],
'passport' => ['url' => 'https://auth.example.com'], // oauth2 flow base URL
'query_builder' => ['pagination_terminals' => ['paginateList']], // extra paginating method names
'permission' => ['enabled' => true], // opt in — off by default
],

Every bag also accepts enabled (bool). It is resolved per document: an integration contributes only when its package is installed and the document enables it. Every integration defaults on when its package is installed, except permission, which defaults off — documenting role and permission names would publish your application’s internal authorization taxonomy, so it is explicit opt-in. When a package is installed but its integration is disabled, the build emits one integration.disabled info diagnostic per document, so the switch is discoverable.

There are eleven toggleable bags, each keyed by its config name — set integrations.<key>.enabled to turn one off (or, for permission, on):

Bag key Package / source enabled default
api_resources Laravel API resources (built in) true
eloquent Eloquent models (built in) true
rate_limit Laravel rate limiting (built in) true
spatie_data spatie/laravel-data true
query_builder spatie/laravel-query-builder true
json_api_paginate spatie/laravel-json-api-paginate true
laravel_actions lorisleiva/laravel-actions true
timacdonald_json_api timacdonald/json-api true
sanctum laravel/sanctum true
passport laravel/passport true
permission spatie/laravel-permission false

The table below lists the additional options each bag accepts beyond enabled.

Bag Key Default Effect
any enabled true (false for permission) Turn the integration on/off for this document. Contributes only when the package is installed and this is true.
api_resources wrap each resource’s own $wrap false never wraps (global withoutWrapping()); true'data'; a string forces that key; omit → each resource’s static $wrap.
sanctum modes ['token','stateful'] Which Sanctum schemes to expose.
sanctum cookie session.cookie Stateful cookie name.
passport url app.url oauth2 flow base URL.
query_builder pagination_terminals [] Extra method names that count as paginating terminals during the trace.
permission enabled false Opt in to document role:/permission: requirements (x-permissions). Off by default so authorization names are not published unintentionally.
'export' => [
'path' => 'docs/openapi.json',
],

path is the default output location for docuccino:export and the file viewer.source: artifact serves. The output format is chosen per run by docuccino:export --format.

'viewer' => [
'route' => '/docs/api', // null disables the runtime endpoints for this document
'gate' => null, // Gate ability name; null = local environment only
'middleware' => ['web', 'throttle:60,1'],
'source' => 'generate', // generate | artifact | cache
// 'cdn' => false, // true loads Scalar from a CDN instead of the bundled asset
],
Key Default Effect
route '/docs/api' Base path for the viewer/spec/asset routes. null disables them for this document.
gate null Gate ability guarding the HTML + .json routes. null = available only in the local environment. The static asset route is never gated.
middleware ['web', 'throttle:60,1'] Middleware for the viewer routes. Keep throttle when exposing the (potentially expensive) spec endpoint publicly. See the warning below if your app is multi-tenant or domain-gated.
source 'generate' generate rebuilds on every request (fine for local/gated); artifact re-emits the committed export.path; cache serves the docuccino:cache-warmed payload (cold cache falls back to generate).
cdn false true loads Scalar from jsDelivr instead of the local bundle.
'versioning' => 'none', // 'semver' | 'date' | 'none'

The policy docuccino:diff --enforce applies to this document. semver requires a major version bump for breaking changes; date requires a new date version; none never fails on versioning. See docuccino:diff.

'extensions' => [],

Class-strings resolved from the container and merged with programmatic Docuccino::extend() registrations at build time, never at boot. See extension authoring.

'lint' => [
'leakage' => [
'enabled' => true,
'allow' => [], // e.g. ['reset_token', '#/components/schemas/Invoice/properties/status']
// 'patterns' => ['sortcode' => 'a bank sort code', 'iban' => 'an IBAN'],
],
],

The data-leakage pass is a diagnostics-only DocumentTransformer (it never mutates output). It warns on schema properties whose names look sensitive (password/token/secret/api_key/…).

Key Default Effect
enabled true Turn the pass on/off.
allow [] Safelist known-good properties by name or JSON pointer.
patterns built-in table Extra token → human-label heuristics merged over the built-in table (key = normalized token, matched when a property name contains it).
'engine' => [
'mode' => env('DOCUCCINO_ENGINE', 'in-process'),
'project_paths' => ['app'],
],
Key Default Effect
mode in-process in-process runs PHPStan; null skips inference entirely (docblocks and attributes still work). Those are the two working modes. Set it per environment with DOCUCCINO_ENGINE. A boot failure degrades to no inference rather than failing the build.
project_paths ['app'] The descend scope: directories the engine follows for general interprocedural analysis (throw classification, inline Validator::make() rules). Bounds descent into callee bodies.

Inference needs the dev-only docuccino/inference-phpstan package. Without it, every mode but null degrades to no inference and each export carries one engine.not-installed warning naming the install command — null is the explicit opt-out and stays silent.

The engine also carries orchestrated and caching worker compositions, but the adapter does not plumb them through yet: setting either warns (engine.mode-not-wired) and runs in-process. Stay on in-process or null.

'cache' => [
'enabled' => false, // OperationFragment cache: incremental builds, off by default
'store' => null, // Laravel cache store for the runtime document cache (docuccino:cache)
// 'path' => null, // fragment cache directory (defaults to storage_path('docuccino/fragments'))
],
Key Effect
enabled Turns on the OperationFragment cache for incremental builds. The key hashes the tool/spec/identity-algo versions, doc config, resolved extension list, route signature, and every dependency file the engine reported — so invalidation is sound even for a Query class three calls deep. Assembly/canonicalize/validate always run fresh.
store Laravel cache store name for the runtime document cache warmed by docuccino:cache.
path Fragment cache directory (defaults to storage_path('docuccino/fragments')).