[{"data":1,"prerenderedAt":1103},["ShallowReactive",2],{"navigation":3,"\u002Fdeployment\u002Fdocker":342,"\u002Fdeployment\u002Fdocker-surround":1098},[4,14,36,69,140,325],{"title":5,"path":6,"stem":7,"children":8},"Introduction","\u002Fgetting-started","1.getting-started\u002F1.index",[9,10],{"title":5,"path":6,"stem":7},{"title":11,"path":12,"stem":13},"Installation","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F2.installation",{"title":15,"path":16,"stem":17,"children":18,"page":35},"Guides","\u002Fguides","2.guides",[19,23,27,31],{"title":20,"path":21,"stem":22},"Your First Layout","\u002Fguides\u002Fyour-first-layout","2.guides\u002F1.your-first-layout",{"title":24,"path":25,"stem":26},"Your First Page Template","\u002Fguides\u002Fyour-first-page-template","2.guides\u002F2.your-first-page-template",{"title":28,"path":29,"stem":30},"Your First Component","\u002Fguides\u002Fyour-first-component","2.guides\u002F3.your-first-component",{"title":32,"path":33,"stem":34},"Alternative UI Variants","\u002Fguides\u002Falternative-ui-variants","2.guides\u002F4.alternative-ui-variants",false,{"title":37,"path":38,"stem":39,"children":40,"page":35},"Core Concepts","\u002Fcore-concepts","3.core-concepts",[41,45,49,53,57,61,65],{"title":42,"path":43,"stem":44},"How It All Works","\u002Fcore-concepts\u002Farchitecture","3.core-concepts\u002F1.architecture",{"title":46,"path":47,"stem":48},"The Data Model","\u002Fcore-concepts\u002Fthe-data-model","3.core-concepts\u002F2.the-data-model",{"title":50,"path":51,"stem":52},"Layouts & Pages","\u002Fcore-concepts\u002Flayouts-and-pages","3.core-concepts\u002F3.layouts-and-pages",{"title":54,"path":55,"stem":56},"Dynamic Pages","\u002Fcore-concepts\u002Fdynamic-pages","3.core-concepts\u002F4.dynamic-pages",{"title":58,"path":59,"stem":60},"Components","\u002Fcore-concepts\u002Fcomponents","3.core-concepts\u002F5.components",{"title":62,"path":63,"stem":64},"Draft & Publish Workflow","\u002Fcore-concepts\u002Fdraft-and-publish","3.core-concepts\u002F6.draft-and-publish",{"title":66,"path":67,"stem":68},"The Admin Panel","\u002Fcore-concepts\u002Fadmin-panel","3.core-concepts\u002F7.admin-panel",{"title":70,"path":71,"stem":72,"children":73,"page":35},"Api","\u002Fapi","4.api",[74,78,116,120,124,128,132,136],{"title":75,"path":76,"stem":77},"Bundle Setup","\u002Fapi\u002Fbundle-setup","4.api\u002F1.bundle-setup",{"title":58,"path":79,"stem":80,"children":81,"page":35},"\u002Fapi\u002Fcomponents","4.api\u002F2.components",[82,86,103],{"title":83,"path":84,"stem":85},"Creating Components","\u002Fapi\u002Fcomponents\u002Fcreating-components","4.api\u002F2.components\u002F1.creating-components",{"title":87,"path":88,"stem":89,"children":90,"page":35},"Annotations","\u002Fapi\u002Fcomponents\u002Fannotations","4.api\u002F2.components\u002F2.annotations",[91,95,99],{"title":92,"path":93,"stem":94},"Publishable","\u002Fapi\u002Fcomponents\u002Fannotations\u002Fpublishable","4.api\u002F2.components\u002F2.annotations\u002F1.publishable",{"title":96,"path":97,"stem":98},"Uploadable","\u002Fapi\u002Fcomponents\u002Fannotations\u002Fuploadable","4.api\u002F2.components\u002F2.annotations\u002F2.uploadable",{"title":100,"path":101,"stem":102},"Timestamped","\u002Fapi\u002Fcomponents\u002Fannotations\u002Ftimestamped","4.api\u002F2.components\u002F2.annotations\u002F3.timestamped",{"title":104,"path":105,"stem":106,"children":107,"page":35},"Built Ins","\u002Fapi\u002Fcomponents\u002Fbuilt-ins","4.api\u002F2.components\u002F3.built-ins",[108,112],{"title":109,"path":110,"stem":111},"Collection Component","\u002Fapi\u002Fcomponents\u002Fbuilt-ins\u002Fcollection-component","4.api\u002F2.components\u002F3.built-ins\u002F1.collection-component",{"title":113,"path":114,"stem":115},"Form Component","\u002Fapi\u002Fcomponents\u002Fbuilt-ins\u002Fform-component","4.api\u002F2.components\u002F3.built-ins\u002F2.form-component",{"title":117,"path":118,"stem":119},"Dynamic & Nested Pages","\u002Fapi\u002Fdynamic-pages","4.api\u002F3.dynamic-pages",{"title":121,"path":122,"stem":123},"Users & Security","\u002Fapi\u002Fusers-and-security","4.api\u002F4.users-and-security",{"title":125,"path":126,"stem":127},"Data Fixtures","\u002Fapi\u002Fdata-fixtures","4.api\u002F5.data-fixtures",{"title":129,"path":130,"stem":131},"Configuration Reference","\u002Fapi\u002Fconfiguration","4.api\u002F6.configuration",{"title":133,"path":134,"stem":135},"Console Commands","\u002Fapi\u002Fconsole-commands","4.api\u002F7.console-commands",{"title":137,"path":138,"stem":139},"Debugging & Profiler","\u002Fapi\u002Fdebugging","4.api\u002F8.debugging",{"title":141,"path":142,"stem":143,"children":144,"page":35},"Nuxt Module","\u002Fnuxt-module","5.nuxt-module",[145,149,162,178,203,207,279,304,308],{"title":146,"path":147,"stem":148},"Module Setup","\u002Fnuxt-module\u002Fmodule-setup","5.nuxt-module\u002F1.module-setup",{"title":150,"path":151,"stem":152,"children":153,"page":35},"Configuration","\u002Fnuxt-module\u002Fconfiguration","5.nuxt-module\u002F2.configuration",[154,158],{"title":155,"path":156,"stem":157},"Nuxt Config","\u002Fnuxt-module\u002Fconfiguration\u002Fnuxt-config","5.nuxt-module\u002F2.configuration\u002F1.nuxt-config",{"title":159,"path":160,"stem":161},"Site Config & SEO","\u002Fnuxt-module\u002Fconfiguration\u002Fsite-config-and-seo","5.nuxt-module\u002F2.configuration\u002F2.site-config-and-seo",{"title":163,"path":164,"stem":165,"children":166,"page":35},"Building Your Ui","\u002Fnuxt-module\u002Fbuilding-your-ui","5.nuxt-module\u002F3.building-your-ui",[167,171,175],{"title":168,"path":169,"stem":170},"Layouts","\u002Fnuxt-module\u002Fbuilding-your-ui\u002Fcreating-layouts","5.nuxt-module\u002F3.building-your-ui\u002F1.creating-layouts",{"title":172,"path":173,"stem":174},"Page Templates","\u002Fnuxt-module\u002Fbuilding-your-ui\u002Fcreating-page-templates","5.nuxt-module\u002F3.building-your-ui\u002F2.creating-page-templates",{"title":83,"path":176,"stem":177},"\u002Fnuxt-module\u002Fbuilding-your-ui\u002Fcreating-components","5.nuxt-module\u002F3.building-your-ui\u002F3.creating-components",{"title":179,"path":180,"stem":181,"children":182,"page":35},"Cwa Components","\u002Fnuxt-module\u002Fcwa-components","5.nuxt-module\u002F4.cwa-components",[183,187,191,195,199],{"title":184,"path":185,"stem":186},"\u003CCwaComponentGroup \u002F>","\u002Fnuxt-module\u002Fcwa-components\u002Fcwa-component-group","5.nuxt-module\u002F4.cwa-components\u002F1.cwa-component-group",{"title":188,"path":189,"stem":190},"\u003CCwaPage \u002F>","\u002Fnuxt-module\u002Fcwa-components\u002Fcwa-page","5.nuxt-module\u002F4.cwa-components\u002F2.cwa-page",{"title":192,"path":193,"stem":194},"\u003CCwaLink \u002F>","\u002Fnuxt-module\u002Fcwa-components\u002Fcwa-link","5.nuxt-module\u002F4.cwa-components\u002F3.cwa-link",{"title":196,"path":197,"stem":198},"\u003CCwaImage \u002F>","\u002Fnuxt-module\u002Fcwa-components\u002Fcwa-image","5.nuxt-module\u002F4.cwa-components\u002F4.cwa-image",{"title":200,"path":201,"stem":202},"\u003CCwaDefaultLayout \u002F>","\u002Fnuxt-module\u002Fcwa-components\u002Fcwa-default-layout","5.nuxt-module\u002F4.cwa-components\u002F5.cwa-default-layout",{"title":204,"path":205,"stem":206},"The useCwa() API","\u002Fnuxt-module\u002Fcwa-api","5.nuxt-module\u002F5.cwa-api",{"title":208,"path":209,"stem":210,"children":211,"page":35},"Composables","\u002Fnuxt-module\u002Fcomposables","5.nuxt-module\u002F6.composables",[212,245,262],{"title":213,"path":214,"stem":215,"children":216,"page":35},"Component","\u002Fnuxt-module\u002Fcomposables\u002Fcomponent","5.nuxt-module\u002F6.composables\u002F1.component",[217,221,225,229,233,237,241],{"title":218,"path":219,"stem":220},"Resource","\u002Fnuxt-module\u002Fcomposables\u002Fcomponent\u002Fuse-cwa-resource","5.nuxt-module\u002F6.composables\u002F1.component\u002F1.use-cwa-resource",{"title":222,"path":223,"stem":224},"Collection Resource","\u002Fnuxt-module\u002Fcomposables\u002Fcomponent\u002Fuse-cwa-collection-resource","5.nuxt-module\u002F6.composables\u002F1.component\u002F2.use-cwa-collection-resource",{"title":226,"path":227,"stem":228},"Image Resource","\u002Fnuxt-module\u002Fcomposables\u002Fcomponent\u002Fuse-cwa-image-resource","5.nuxt-module\u002F6.composables\u002F1.component\u002F3.use-cwa-image-resource",{"title":230,"path":231,"stem":232},"Form","\u002Fnuxt-module\u002Fcomposables\u002Fcomponent\u002Fuse-cwa-form","5.nuxt-module\u002F6.composables\u002F1.component\u002F4.use-cwa-form",{"title":234,"path":235,"stem":236},"Form Input","\u002Fnuxt-module\u002Fcomposables\u002Fcomponent\u002Fuse-cwa-form-input","5.nuxt-module\u002F6.composables\u002F1.component\u002F5.use-cwa-form-input",{"title":238,"path":239,"stem":240},"Form Repeated","\u002Fnuxt-module\u002Fcomposables\u002Fcomponent\u002Fuse-cwa-form-repeated","5.nuxt-module\u002F6.composables\u002F1.component\u002F6.use-cwa-form-repeated",{"title":242,"path":243,"stem":244},"Form Collection","\u002Fnuxt-module\u002Fcomposables\u002Fcomponent\u002Fuse-cwa-form-collection","5.nuxt-module\u002F6.composables\u002F1.component\u002F7.use-cwa-form-collection",{"title":246,"path":247,"stem":248,"children":249,"page":35},"Admin Manager","\u002Fnuxt-module\u002Fcomposables\u002Fadmin-manager","5.nuxt-module\u002F6.composables\u002F2.admin-manager",[250,254,258],{"title":251,"path":252,"stem":253},"Manager Tab","\u002Fnuxt-module\u002Fcomposables\u002Fadmin-manager\u002Fuse-cwa-resource-manager-tab","5.nuxt-module\u002F6.composables\u002F2.admin-manager\u002F1.use-cwa-resource-manager-tab",{"title":255,"path":256,"stem":257},"Resource Model","\u002Fnuxt-module\u002Fcomposables\u002Fadmin-manager\u002Fuse-cwa-resource-model","5.nuxt-module\u002F6.composables\u002F2.admin-manager\u002F2.use-cwa-resource-model",{"title":259,"path":260,"stem":261},"Resource Upload","\u002Fnuxt-module\u002Fcomposables\u002Fadmin-manager\u002Fuse-cwa-resource-upload","5.nuxt-module\u002F6.composables\u002F2.admin-manager\u002F3.use-cwa-resource-upload",{"title":263,"path":264,"stem":265,"children":266,"page":35},"Utilities","\u002Fnuxt-module\u002Fcomposables\u002Futilities","5.nuxt-module\u002F6.composables\u002F3.utilities",[267,271,275],{"title":268,"path":269,"stem":270},"Resource Endpoint","\u002Fnuxt-module\u002Fcomposables\u002Futilities\u002Fuse-cwa-resource-endpoint","5.nuxt-module\u002F6.composables\u002F3.utilities\u002F1.use-cwa-resource-endpoint",{"title":272,"path":273,"stem":274},"Query Model","\u002Fnuxt-module\u002Fcomposables\u002Futilities\u002Fuse-query-bound-model","5.nuxt-module\u002F6.composables\u002F3.utilities\u002F2.use-query-bound-model",{"title":276,"path":277,"stem":278},"Resource Route","\u002Fnuxt-module\u002Fcomposables\u002Futilities\u002Fuse-cwa-resource-route","5.nuxt-module\u002F6.composables\u002F3.utilities\u002F3.use-cwa-resource-route",{"title":280,"path":281,"stem":282,"children":283,"page":35},"Component Helpers","\u002Fnuxt-module\u002Fcomponent-helpers","5.nuxt-module\u002F7.component-helpers",[284,288,292,296,300],{"title":285,"path":286,"stem":287},"Images & Media","\u002Fnuxt-module\u002Fcomponent-helpers\u002Fimages-and-uploads","5.nuxt-module\u002F7.component-helpers\u002F1.images-and-uploads",{"title":289,"path":290,"stem":291},"Collections & Pagination","\u002Fnuxt-module\u002Fcomponent-helpers\u002Fcollections-and-pagination","5.nuxt-module\u002F7.component-helpers\u002F2.collections-and-pagination",{"title":293,"path":294,"stem":295},"HTML Content","\u002Fnuxt-module\u002Fcomponent-helpers\u002Fhtml-content","5.nuxt-module\u002F7.component-helpers\u002F3.html-content",{"title":297,"path":298,"stem":299},"Real-Time Updates","\u002Fnuxt-module\u002Fcomponent-helpers\u002Freal-time-updates","5.nuxt-module\u002F7.component-helpers\u002F4.real-time-updates",{"title":301,"path":302,"stem":303},"Forms","\u002Fnuxt-module\u002Fcomponent-helpers\u002Fforms","5.nuxt-module\u002F7.component-helpers\u002F5.forms",{"title":305,"path":306,"stem":307},"Authentication","\u002Fnuxt-module\u002Fauthentication","5.nuxt-module\u002F8.authentication",{"title":309,"path":310,"stem":311,"children":312,"page":35},"Cwa Layer","\u002Fnuxt-module\u002Fcwa-layer","5.nuxt-module\u002F9.cwa-layer",[313,317,321],{"title":314,"path":315,"stem":316},"Overview","\u002Fnuxt-module\u002Fcwa-layer\u002Foverview","5.nuxt-module\u002F9.cwa-layer\u002F1.overview",{"title":318,"path":319,"stem":320},"Auth Pages","\u002Fnuxt-module\u002Fcwa-layer\u002Fauth-pages","5.nuxt-module\u002F9.cwa-layer\u002F2.auth-pages",{"title":322,"path":323,"stem":324},"Admin Panel","\u002Fnuxt-module\u002Fcwa-layer\u002Fadmin-panel","5.nuxt-module\u002F9.cwa-layer\u002F3.admin-panel",{"title":326,"path":327,"stem":328,"children":329,"page":35},"Deployment","\u002Fdeployment","6.deployment",[330,334,338],{"title":331,"path":332,"stem":333},"Docker","\u002Fdeployment\u002Fdocker","6.deployment\u002F1.docker",{"title":335,"path":336,"stem":337},"Kubernetes & Helm","\u002Fdeployment\u002Fkubernetes","6.deployment\u002F2.kubernetes",{"title":339,"path":340,"stem":341},"CI\u002FCD","\u002Fdeployment\u002Fci-cd","6.deployment\u002F3.ci-cd",{"id":343,"title":331,"badge":344,"body":347,"description":1092,"extension":1093,"links":1094,"meta":1095,"navigation":498,"path":332,"seo":1096,"stem":333,"__hash__":1097},"docs\u002F6.deployment\u002F1.docker.md",{"label":345,"color":346},"Draft","amber",{"type":348,"value":349,"toc":1082},"minimark",[350,354,359,448,452,470,599,610,614,719,723,729,759,763,766,811,821,825,828,887,892,913,918,950,957,961,971,992,995,1030,1034,1044,1050,1060,1078],[351,352,353],"p",{},"The CWA template repository ships a complete Docker Compose stack. Clone the template and you have a fully wired local environment — PHP, Nuxt, Mercure, PostgreSQL, and a reverse proxy — ready in minutes.",[355,356,358],"h2",{"id":357},"services","Services",[360,361,362,378],"table",{},[363,364,365],"thead",{},[366,367,368,372,375],"tr",{},[369,370,371],"th",{},"Service",[369,373,374],{},"Image",[369,376,377],{},"Role",[379,380,381,396,409,422,435],"tbody",{},[366,382,383,390,393],{},[384,385,386],"td",{},[387,388,389],"code",{},"php",[384,391,392],{},"FrankenPHP",[384,394,395],{},"Symfony API (HTTP + Mercure publisher)",[366,397,398,403,406],{},[384,399,400],{},[387,401,402],{},"nuxt",[384,404,405],{},"Node 22",[384,407,408],{},"Nuxt SSR application",[366,410,411,416,419],{},[384,412,413],{},[387,414,415],{},"database",[384,417,418],{},"PostgreSQL 16",[384,420,421],{},"Primary database",[366,423,424,429,432],{},[384,425,426],{},[387,427,428],{},"mercure",[384,430,431],{},"Caddy + Mercure module",[384,433,434],{},"Real-time hub",[366,436,437,442,445],{},[384,438,439],{},[387,440,441],{},"traefik",[384,443,444],{},"Traefik",[384,446,447],{},"Reverse proxy routing",[355,449,451],{"id":450},"environment-variables","Environment Variables",[351,453,454,455,458,459,462,463,469],{},"Create ",[387,456,457],{},".env.local"," alongside ",[387,460,461],{},".env"," — ",[464,465,466,467],"strong",{},"never commit ",[387,468,457],{},":",[471,472,477],"pre",{"className":473,"code":474,"language":475,"meta":476,"style":476},"language-ini shiki shiki-themes github-light github-dark material-theme-palenight","# Database\nDATABASE_URL=postgresql:\u002F\u002Fapp:secret@database:5432\u002Fapp?serverVersion=16&charset=utf8\n\n# JWT Authentication\nJWT_SECRET_KEY=%kernel.project_dir%\u002Fconfig\u002Fjwt\u002Fprivate.pem\nJWT_PUBLIC_KEY=%kernel.project_dir%\u002Fconfig\u002Fjwt\u002Fpublic.pem\nJWT_PASSPHRASE=your_jwt_passphrase\nJWT_COOKIE_SAMESITE=strict\n\n# Mercure\nMERCURE_URL=http:\u002F\u002Fmercure\u002F.well-known\u002Fmercure\nMERCURE_PUBLIC_URL=https:\u002F\u002Fmercure.localhost\u002F.well-known\u002Fmercure\nMERCURE_JWT_SECRET=your_mercure_secret\n\n# Nuxt (server-side API URL = internal Docker network; browser URL = public)\nNUXT_PUBLIC_CWA_API_URL=http:\u002F\u002Fphp\nNUXT_PUBLIC_CWA_API_URL_BROWSER=https:\u002F\u002Fapi.localhost\n\n# Email\nMAILER_DSN=smtp:\u002F\u002Flocalhost:1025\n","ini","",[387,478,479,487,493,500,506,512,518,524,530,535,541,547,553,559,564,570,576,582,587,593],{"__ignoreMap":476},[480,481,484],"span",{"class":482,"line":483},"line",1,[480,485,486],{},"# Database\n",[480,488,490],{"class":482,"line":489},2,[480,491,492],{},"DATABASE_URL=postgresql:\u002F\u002Fapp:secret@database:5432\u002Fapp?serverVersion=16&charset=utf8\n",[480,494,496],{"class":482,"line":495},3,[480,497,499],{"emptyLinePlaceholder":498},true,"\n",[480,501,503],{"class":482,"line":502},4,[480,504,505],{},"# JWT Authentication\n",[480,507,509],{"class":482,"line":508},5,[480,510,511],{},"JWT_SECRET_KEY=%kernel.project_dir%\u002Fconfig\u002Fjwt\u002Fprivate.pem\n",[480,513,515],{"class":482,"line":514},6,[480,516,517],{},"JWT_PUBLIC_KEY=%kernel.project_dir%\u002Fconfig\u002Fjwt\u002Fpublic.pem\n",[480,519,521],{"class":482,"line":520},7,[480,522,523],{},"JWT_PASSPHRASE=your_jwt_passphrase\n",[480,525,527],{"class":482,"line":526},8,[480,528,529],{},"JWT_COOKIE_SAMESITE=strict\n",[480,531,533],{"class":482,"line":532},9,[480,534,499],{"emptyLinePlaceholder":498},[480,536,538],{"class":482,"line":537},10,[480,539,540],{},"# Mercure\n",[480,542,544],{"class":482,"line":543},11,[480,545,546],{},"MERCURE_URL=http:\u002F\u002Fmercure\u002F.well-known\u002Fmercure\n",[480,548,550],{"class":482,"line":549},12,[480,551,552],{},"MERCURE_PUBLIC_URL=https:\u002F\u002Fmercure.localhost\u002F.well-known\u002Fmercure\n",[480,554,556],{"class":482,"line":555},13,[480,557,558],{},"MERCURE_JWT_SECRET=your_mercure_secret\n",[480,560,562],{"class":482,"line":561},14,[480,563,499],{"emptyLinePlaceholder":498},[480,565,567],{"class":482,"line":566},15,[480,568,569],{},"# Nuxt (server-side API URL = internal Docker network; browser URL = public)\n",[480,571,573],{"class":482,"line":572},16,[480,574,575],{},"NUXT_PUBLIC_CWA_API_URL=http:\u002F\u002Fphp\n",[480,577,579],{"class":482,"line":578},17,[480,580,581],{},"NUXT_PUBLIC_CWA_API_URL_BROWSER=https:\u002F\u002Fapi.localhost\n",[480,583,585],{"class":482,"line":584},18,[480,586,499],{"emptyLinePlaceholder":498},[480,588,590],{"class":482,"line":589},19,[480,591,592],{},"# Email\n",[480,594,596],{"class":482,"line":595},20,[480,597,598],{},"MAILER_DSN=smtp:\u002F\u002Flocalhost:1025\n",[351,600,601,602,605,606,609],{},"The two CWA API URL variables are intentionally different in Docker Compose: ",[387,603,604],{},"NUXT_PUBLIC_CWA_API_URL"," is the internal Docker hostname (Nuxt → PHP on the Docker network), while ",[387,607,608],{},"NUXT_PUBLIC_CWA_API_URL_BROWSER"," is the public-facing URL clients use from their browsers.",[355,611,613],{"id":612},"starting-the-stack","Starting the Stack",[471,615,619],{"className":616,"code":617,"language":618,"meta":476,"style":476},"language-bash shiki shiki-themes github-light github-dark material-theme-palenight","# Start all services in the background\ndocker compose up -d\n\n# Run database migrations\ndocker compose exec php bin\u002Fconsole doctrine:migrations:migrate\n\n# Create your first admin user\ndocker compose exec php bin\u002Fconsole silverback:api-components:user:create\n\n# Load fixtures (optional)\ndocker compose exec php bin\u002Fconsole doctrine:fixtures:load\n","bash",[387,620,621,627,644,648,653,671,675,680,695,699,704],{"__ignoreMap":476},[480,622,623],{"class":482,"line":483},[480,624,626],{"class":625},"sTBSN","# Start all services in the background\n",[480,628,629,633,637,640],{"class":482,"line":489},[480,630,632],{"class":631},"sRCss","docker",[480,634,636],{"class":635},"sLL54"," compose",[480,638,639],{"class":635}," up",[480,641,643],{"class":642},"szhYu"," -d\n",[480,645,646],{"class":482,"line":495},[480,647,499],{"emptyLinePlaceholder":498},[480,649,650],{"class":482,"line":502},[480,651,652],{"class":625},"# Run database migrations\n",[480,654,655,657,659,662,665,668],{"class":482,"line":508},[480,656,632],{"class":631},[480,658,636],{"class":635},[480,660,661],{"class":635}," exec",[480,663,664],{"class":635}," php",[480,666,667],{"class":635}," bin\u002Fconsole",[480,669,670],{"class":635}," doctrine:migrations:migrate\n",[480,672,673],{"class":482,"line":514},[480,674,499],{"emptyLinePlaceholder":498},[480,676,677],{"class":482,"line":520},[480,678,679],{"class":625},"# Create your first admin user\n",[480,681,682,684,686,688,690,692],{"class":482,"line":526},[480,683,632],{"class":631},[480,685,636],{"class":635},[480,687,661],{"class":635},[480,689,664],{"class":635},[480,691,667],{"class":635},[480,693,694],{"class":635}," silverback:api-components:user:create\n",[480,696,697],{"class":482,"line":532},[480,698,499],{"emptyLinePlaceholder":498},[480,700,701],{"class":482,"line":537},[480,702,703],{"class":625},"# Load fixtures (optional)\n",[480,705,706,708,710,712,714,716],{"class":482,"line":543},[480,707,632],{"class":631},[480,709,636],{"class":635},[480,711,661],{"class":635},[480,713,664],{"class":635},[480,715,667],{"class":635},[480,717,718],{"class":635}," doctrine:fixtures:load\n",[355,720,722],{"id":721},"development-workflow","Development Workflow",[351,724,725,728],{},[387,726,727],{},"docker-compose.override.yml"," mounts source directories into the containers:",[730,731,732,743,753],"ul",{},[733,734,735,738,739,742],"li",{},[464,736,737],{},"PHP",": your ",[387,740,741],{},"api\u002F"," directory is mounted; PHP changes are reflected immediately (no build step)",[733,744,745,748,749,752],{},[464,746,747],{},"Nuxt",": ",[387,750,751],{},"app\u002F"," is mounted; Vite HMR updates the browser on save",[733,754,755,758],{},[464,756,757],{},"Xdebug",": configured in the override file; connect via your IDE on port 9003",[355,760,762],{"id":761},"jwt-key-generation","JWT Key Generation",[351,764,765],{},"Generate JWT keys once per environment before first start:",[471,767,769],{"className":616,"code":768,"language":618,"meta":476,"style":476},"docker compose exec php bash -c \"\n    mkdir -p config\u002Fjwt\n    openssl genpkey -out config\u002Fjwt\u002Fprivate.pem -aes256 -algorithm rsa -pkeyopt rsa_keygen_bits:4096\n    openssl pkey -in config\u002Fjwt\u002Fprivate.pem -out config\u002Fjwt\u002Fpublic.pem -pubout\n\"\n",[387,770,771,791,796,801,806],{"__ignoreMap":476},[480,772,773,775,777,779,781,784,787],{"class":482,"line":483},[480,774,632],{"class":631},[480,776,636],{"class":635},[480,778,661],{"class":635},[480,780,664],{"class":635},[480,782,783],{"class":635}," bash",[480,785,786],{"class":642}," -c",[480,788,790],{"class":789},"seSrl"," \"\n",[480,792,793],{"class":482,"line":489},[480,794,795],{"class":635},"    mkdir -p config\u002Fjwt\n",[480,797,798],{"class":482,"line":495},[480,799,800],{"class":635},"    openssl genpkey -out config\u002Fjwt\u002Fprivate.pem -aes256 -algorithm rsa -pkeyopt rsa_keygen_bits:4096\n",[480,802,803],{"class":482,"line":502},[480,804,805],{"class":635},"    openssl pkey -in config\u002Fjwt\u002Fprivate.pem -out config\u002Fjwt\u002Fpublic.pem -pubout\n",[480,807,808],{"class":482,"line":508},[480,809,810],{"class":789},"\"\n",[351,812,813,814,817,818,820],{},"The passphrase you use must match ",[387,815,816],{},"JWT_PASSPHRASE"," in ",[387,819,457],{},".",[355,822,824],{"id":823},"building-for-production","Building for Production",[351,826,827],{},"Multi-stage Dockerfiles produce lean production images:",[471,829,831],{"className":616,"code":830,"language":618,"meta":476,"style":476},"# Build PHP image\ndocker build --target runner -t ghcr.io\u002Fyour-org\u002Fapp-php:latest .\u002Fapi\n\n# Build Nuxt image\ndocker build --target runner -t ghcr.io\u002Fyour-org\u002Fapp-nuxt:latest .\u002Fapp\n",[387,832,833,838,860,864,869],{"__ignoreMap":476},[480,834,835],{"class":482,"line":483},[480,836,837],{"class":625},"# Build PHP image\n",[480,839,840,842,845,848,851,854,857],{"class":482,"line":489},[480,841,632],{"class":631},[480,843,844],{"class":635}," build",[480,846,847],{"class":642}," --target",[480,849,850],{"class":635}," runner",[480,852,853],{"class":642}," -t",[480,855,856],{"class":635}," ghcr.io\u002Fyour-org\u002Fapp-php:latest",[480,858,859],{"class":635}," .\u002Fapi\n",[480,861,862],{"class":482,"line":495},[480,863,499],{"emptyLinePlaceholder":498},[480,865,866],{"class":482,"line":502},[480,867,868],{"class":625},"# Build Nuxt image\n",[480,870,871,873,875,877,879,881,884],{"class":482,"line":508},[480,872,632],{"class":631},[480,874,844],{"class":635},[480,876,847],{"class":642},[480,878,850],{"class":635},[480,880,853],{"class":642},[480,882,883],{"class":635}," ghcr.io\u002Fyour-org\u002Fapp-nuxt:latest",[480,885,886],{"class":635}," .\u002Fapp\n",[351,888,889],{},[464,890,891],{},"PHP Dockerfile stages:",[893,894,895,901,907],"ol",{},[733,896,897,900],{},[387,898,899],{},"deps"," — install Composer dependencies",[733,902,903,906],{},[387,904,905],{},"builder"," — copy app, remove dev dependencies",[733,908,909,912],{},[387,910,911],{},"runner"," — FrankenPHP with production config",[351,914,915],{},[464,916,917],{},"Nuxt Dockerfile stages:",[893,919,920,927,939],{},[733,921,922,462,924],{},[387,923,899],{},[387,925,926],{},"pnpm install",[733,928,929,462,931,934,935,938],{},[387,930,905],{},[387,932,933],{},"pnpm build"," (outputs ",[387,936,937],{},".output\u002F",")",[733,940,941,943,944,946,947],{},[387,942,911],{}," — copies ",[387,945,937],{},", runs ",[387,948,949],{},"node .output\u002Fserver\u002Findex.mjs",[351,951,952,953,956],{},"Migrations are ",[464,954,955],{},"not"," run at image build time. Run them as a separate step in your deploy process before starting the new containers.",[355,958,960],{"id":959},"production-docker-compose","Production Docker Compose",[351,962,963,964,966,967,970],{},"For production, remove the ",[387,965,727],{}," and use the base ",[387,968,969],{},"docker-compose.yml"," with your production environment file:",[471,972,974],{"className":616,"code":973,"language":618,"meta":476,"style":476},"docker compose --env-file .env.production up -d\n",[387,975,976],{"__ignoreMap":476},[480,977,978,980,982,985,988,990],{"class":482,"line":483},[480,979,632],{"class":631},[480,981,636],{"class":635},[480,983,984],{"class":642}," --env-file",[480,986,987],{"class":635}," .env.production",[480,989,639],{"class":635},[480,991,643],{"class":642},[351,993,994],{},"Tag images with the git SHA for immutable, rollback-capable deploys:",[471,996,998],{"className":616,"code":997,"language":618,"meta":476,"style":476},"docker build -t ghcr.io\u002Fyour-org\u002Fapp-php:$GIT_SHA .\u002Fapi\ndocker push ghcr.io\u002Fyour-org\u002Fapp-php:$GIT_SHA\n",[387,999,1000,1018],{"__ignoreMap":476},[480,1001,1002,1004,1006,1008,1011,1015],{"class":482,"line":483},[480,1003,632],{"class":631},[480,1005,844],{"class":635},[480,1007,853],{"class":642},[480,1009,1010],{"class":635}," ghcr.io\u002Fyour-org\u002Fapp-php:",[480,1012,1014],{"class":1013},"sPB8G","$GIT_SHA ",[480,1016,1017],{"class":635},".\u002Fapi\n",[480,1019,1020,1022,1025,1027],{"class":482,"line":489},[480,1021,632],{"class":631},[480,1023,1024],{"class":635}," push",[480,1026,1010],{"class":635},[480,1028,1029],{"class":1013},"$GIT_SHA\n",[355,1031,1033],{"id":1032},"common-gotchas","Common Gotchas",[351,1035,1036,1043],{},[464,1037,1038,1040,1041],{},[387,1039,604],{}," vs ",[387,1042,608],{},": Must be different when your API is on an internal Docker hostname. The server-side URL uses the Docker service name; the browser URL must be the public domain.",[351,1045,1046,1049],{},[464,1047,1048],{},"JWT keys",": Generate once and mount as a secret — never bake private keys into the image.",[351,1051,1052,1055,1056,1059],{},[464,1053,1054],{},"Database migrations on restart",": Don't run migrations in the container ",[387,1057,1058],{},"CMD",". Race conditions occur when multiple pods start simultaneously. Run them as a pre-deploy Job.",[351,1061,1062,1065,1066,1069,1070,1073,1074,1077],{},[464,1063,1064],{},"Mercure cookie SameSite",": Set ",[387,1067,1068],{},"JWT_COOKIE_SAMESITE=none"," and ",[387,1071,1072],{},"Secure: true"," if your API and front-end are on different subdomains. On the same domain, ",[387,1075,1076],{},"strict"," is safe.",[1079,1080,1081],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sTBSN, html code.shiki .sTBSN{--shiki-light:#6A737D;--shiki-light-font-style:inherit;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sRCss, html code.shiki .sRCss{--shiki-light:#6F42C1;--shiki-default:#B392F0;--shiki-dark:#FFCB6B}html pre.shiki code .sLL54, html code.shiki .sLL54{--shiki-light:#032F62;--shiki-default:#9ECBFF;--shiki-dark:#C3E88D}html pre.shiki code .szhYu, html code.shiki .szhYu{--shiki-light:#005CC5;--shiki-default:#79B8FF;--shiki-dark:#C3E88D}html pre.shiki code .seSrl, html code.shiki .seSrl{--shiki-light:#032F62;--shiki-default:#9ECBFF;--shiki-dark:#89DDFF}html pre.shiki code .sPB8G, html code.shiki .sPB8G{--shiki-light:#24292E;--shiki-default:#E1E4E8;--shiki-dark:#BABED8}",{"title":476,"searchDepth":489,"depth":489,"links":1083},[1084,1085,1086,1087,1088,1089,1090,1091],{"id":357,"depth":489,"text":358},{"id":450,"depth":489,"text":451},{"id":612,"depth":489,"text":613},{"id":721,"depth":489,"text":722},{"id":761,"depth":489,"text":762},{"id":823,"depth":489,"text":824},{"id":959,"depth":489,"text":960},{"id":1032,"depth":489,"text":1033},"The Docker Compose setup for local development and production — services, environment variables, volumes, and build workflow.","md",null,{},{"title":331,"description":1092},"gc4UJiKgijz9_MtCduma2K5l2gYhkeWtdcH0RSpE-QY",[1099,1101],{"title":322,"path":323,"stem":324,"description":1100,"children":-1},"The built-in CWA admin — pages, edit mode, the resource manager, and how to extend it with custom manager tabs.",{"title":335,"path":336,"stem":337,"description":1102,"children":-1},"Deploying the CWA stack to Kubernetes using Helm — values configuration, secrets management, migration Jobs, and rolling updates.",1782241286545]