[{"data":1,"prerenderedAt":1056},["ShallowReactive",2],{"navigation_docs_en":3,"-en-cli-add-surface":342,"-en-cli-add-surface-surround":1051},[4,45,61,96,132,166,196,217,241,265],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":44},"Getting Started","i-lucide-rocket","\u002Fen\u002Fgetting-started","en\u002F1.getting-started",[10,15,20,24,29,34,39],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fen\u002Fgetting-started\u002Fintroduction","en\u002F1.getting-started\u002F1.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Coming from Laravel","\u002Fen\u002Fgetting-started\u002Fcoming-from-laravel","en\u002F1.getting-started\u002F2.coming-from-laravel","i-lucide-arrow-right-left",{"title":21,"path":22,"stem":23,"icon":19},"Coming from Symfony","\u002Fen\u002Fgetting-started\u002Fcoming-from-symfony","en\u002F1.getting-started\u002F3.coming-from-symfony",{"title":25,"path":26,"stem":27,"icon":28},"Installation","\u002Fen\u002Fgetting-started\u002Finstallation","en\u002F1.getting-started\u002F4.installation","i-lucide-download",{"title":30,"path":31,"stem":32,"icon":33},"Your first module","\u002Fen\u002Fgetting-started\u002Ffirst-module","en\u002F1.getting-started\u002F5.first-module","i-lucide-package-plus",{"title":35,"path":36,"stem":37,"icon":38},"Project structure","\u002Fen\u002Fgetting-started\u002Fproject-structure","en\u002F1.getting-started\u002F6.project-structure","i-lucide-folder-tree",{"title":40,"path":41,"stem":42,"icon":43},"The shop sample application","\u002Fen\u002Fgetting-started\u002Fsample-app","en\u002F1.getting-started\u002F7.sample-app","i-lucide-shopping-cart",false,{"title":46,"icon":47,"path":48,"stem":49,"children":50,"page":44},"Reference","i-lucide-book-open","\u002Fen\u002Freference","en\u002F10.reference",[51,56],{"title":52,"path":53,"stem":54,"icon":55},"Configuration","\u002Fen\u002Freference\u002Fconfiguration","en\u002F10.reference\u002F1.configuration","i-lucide-settings",{"title":57,"path":58,"stem":59,"icon":60},"External dependencies","\u002Fen\u002Freference\u002Fdependencies","en\u002F10.reference\u002F2.dependencies","i-lucide-package",{"title":62,"icon":63,"path":64,"stem":65,"children":66,"page":44},"Concepts","i-lucide-lightbulb","\u002Fen\u002Fconcepts","en\u002F2.concepts",[67,72,77,82,86,91],{"title":68,"path":69,"stem":70,"icon":71},"Architecture","\u002Fen\u002Fconcepts\u002Farchitecture","en\u002F2.concepts\u002F1.architecture","i-lucide-layers",{"title":73,"path":74,"stem":75,"icon":76},"Application lifecycle","\u002Fen\u002Fconcepts\u002Fapplication-lifecycle","en\u002F2.concepts\u002F2.application-lifecycle","i-lucide-power",{"title":78,"path":79,"stem":80,"icon":81},"Error model","\u002Fen\u002Fconcepts\u002Ferror-model","en\u002F2.concepts\u002F3.error-model","i-lucide-shield-alert",{"title":52,"path":83,"stem":84,"icon":85},"\u002Fen\u002Fconcepts\u002Fconfiguration","en\u002F2.concepts\u002F4.configuration","i-lucide-settings-2",{"title":87,"path":88,"stem":89,"icon":90},"Codegen pipeline","\u002Fen\u002Fconcepts\u002Fcodegen-pipeline","en\u002F2.concepts\u002F5.codegen-pipeline","i-lucide-file-json",{"title":92,"path":93,"stem":94,"icon":95},"Design patterns","\u002Fen\u002Fconcepts\u002Fdesign-patterns","en\u002F2.concepts\u002F6.design-patterns","i-lucide-puzzle",{"title":97,"icon":98,"path":99,"stem":100,"children":101,"page":44},"HTTP & Routing","i-lucide-globe","\u002Fen\u002Fhttp","en\u002F3.http",[102,107,112,117,122,127],{"title":103,"path":104,"stem":105,"icon":106},"The server core","\u002Fen\u002Fhttp\u002Fserver","en\u002F3.http\u002F1.server","i-lucide-server",{"title":108,"path":109,"stem":110,"icon":111},"The Fiber engine","\u002Fen\u002Fhttp\u002Ffiber","en\u002F3.http\u002F2.fiber","i-lucide-zap",{"title":113,"path":114,"stem":115,"icon":116},"The chi engine","\u002Fen\u002Fhttp\u002Fchi","en\u002F3.http\u002F3.chi","i-lucide-route",{"title":118,"path":119,"stem":120,"icon":121},"Error responses","\u002Fen\u002Fhttp\u002Ferror-responses","en\u002F3.http\u002F4.error-responses","i-lucide-octagon-alert",{"title":123,"path":124,"stem":125,"icon":126},"Validation","\u002Fen\u002Fhttp\u002Fvalidation","en\u002F3.http\u002F5.validation","i-lucide-badge-check",{"title":128,"path":129,"stem":130,"icon":131},"Pagination","\u002Fen\u002Fhttp\u002Fpagination","en\u002F3.http\u002F6.pagination","i-lucide-list-ordered",{"title":133,"icon":134,"path":135,"stem":136,"children":137,"page":44},"Database","i-lucide-database","\u002Fen\u002Fdatabase","en\u002F4.database",[138,142,147,151,156,161],{"title":139,"path":140,"stem":141,"icon":134},"Overview","\u002Fen\u002Fdatabase\u002Foverview","en\u002F4.database\u002F1.overview",{"title":143,"path":144,"stem":145,"icon":146},"pgx","\u002Fen\u002Fdatabase\u002Fpgx","en\u002F4.database\u002F2.pgx","i-lucide-plug",{"title":148,"path":149,"stem":150,"icon":71},"bun","\u002Fen\u002Fdatabase\u002Fbun","en\u002F4.database\u002F3.bun",{"title":152,"path":153,"stem":154,"icon":155},"Transactions","\u002Fen\u002Fdatabase\u002Ftransactions","en\u002F4.database\u002F4.transactions","i-lucide-git-merge",{"title":157,"path":158,"stem":159,"icon":160},"Migrations","\u002Fen\u002Fdatabase\u002Fmigrations","en\u002F4.database\u002F5.migrations","i-lucide-file-stack",{"title":162,"path":163,"stem":164,"icon":165},"Seeders","\u002Fen\u002Fdatabase\u002Fseeding","en\u002F4.database\u002F6.seeding","i-lucide-sprout",{"title":167,"icon":168,"path":169,"stem":170,"children":171,"page":44},"Async & Background","i-lucide-workflow","\u002Fen\u002Fasync","en\u002F5.async",[172,176,181,186,191],{"title":173,"path":174,"stem":175,"icon":131},"Queue","\u002Fen\u002Fasync\u002Fqueue","en\u002F5.async\u002F1.queue",{"title":177,"path":178,"stem":179,"icon":180},"Events","\u002Fen\u002Fasync\u002Fevents","en\u002F5.async\u002F2.events","i-lucide-radio",{"title":182,"path":183,"stem":184,"icon":185},"Outbox","\u002Fen\u002Fasync\u002Foutbox","en\u002F5.async\u002F3.outbox","i-lucide-inbox",{"title":187,"path":188,"stem":189,"icon":190},"Scheduling","\u002Fen\u002Fasync\u002Fscheduler","en\u002F5.async\u002F4.scheduler","i-lucide-calendar-clock",{"title":192,"path":193,"stem":194,"icon":195},"Worker","\u002Fen\u002Fasync\u002Fworker","en\u002F5.async\u002F5.worker","i-lucide-cog",{"title":197,"icon":198,"path":199,"stem":200,"children":201,"page":44},"Security","i-lucide-shield-check","\u002Fen\u002Fsecurity","en\u002F6.security",[202,207,212],{"title":203,"path":204,"stem":205,"icon":206},"Authentication","\u002Fen\u002Fsecurity\u002Fauthentication","en\u002F6.security\u002F1.authentication","i-lucide-key-round",{"title":208,"path":209,"stem":210,"icon":211},"RBAC","\u002Fen\u002Fsecurity\u002Frbac","en\u002F6.security\u002F2.rbac","i-lucide-users",{"title":213,"path":214,"stem":215,"icon":216},"Policies","\u002Fen\u002Fsecurity\u002Fpolicies","en\u002F6.security\u002F3.policies","i-lucide-gavel",{"title":218,"icon":219,"path":220,"stem":221,"children":222,"page":44},"Observability","i-lucide-activity","\u002Fen\u002Fobservability","en\u002F7.observability",[223,226,231,236],{"title":139,"path":224,"stem":225,"icon":219},"\u002Fen\u002Fobservability\u002Foverview","en\u002F7.observability\u002F1.overview",{"title":227,"path":228,"stem":229,"icon":230},"Logging","\u002Fen\u002Fobservability\u002Flogging","en\u002F7.observability\u002F2.logging","i-lucide-scroll-text",{"title":232,"path":233,"stem":234,"icon":235},"OpenTelemetry","\u002Fen\u002Fobservability\u002Fopentelemetry","en\u002F7.observability\u002F3.opentelemetry","i-lucide-radar",{"title":237,"path":238,"stem":239,"icon":240},"Sentry","\u002Fen\u002Fobservability\u002Fsentry","en\u002F7.observability\u002F4.sentry","i-lucide-bug",{"title":242,"icon":243,"path":244,"stem":245,"children":246,"page":44},"Storage & Mail","i-lucide-hard-drive","\u002Fen\u002Fstorage-mail","en\u002F8.storage-mail",[247,251,256,261],{"title":248,"path":249,"stem":250,"icon":243},"Storage","\u002Fen\u002Fstorage-mail\u002Fstorage","en\u002F8.storage-mail\u002F1.storage",{"title":252,"path":253,"stem":254,"icon":255},"Email","\u002Fen\u002Fstorage-mail\u002Fmail","en\u002F8.storage-mail\u002F2.mail","i-lucide-mail",{"title":257,"path":258,"stem":259,"icon":260},"Notifications","\u002Fen\u002Fstorage-mail\u002Fnotifications","en\u002F8.storage-mail\u002F3.notifications","i-lucide-bell",{"title":262,"path":263,"stem":264,"icon":180},"Realtime","\u002Fen\u002Fstorage-mail\u002Frealtime","en\u002F8.storage-mail\u002F4.realtime",{"title":266,"icon":267,"path":268,"stem":269,"children":270,"page":44},"CLI Reference","i-lucide-terminal","\u002Fen\u002Fcli","en\u002F9.cli",[271,274,279,284,289,294,299,303,308,313,318,323,328,333,337],{"title":139,"path":272,"stem":273,"icon":267},"\u002Fen\u002Fcli\u002Foverview","en\u002F9.cli\u002F1.overview",{"title":275,"path":276,"stem":277,"icon":278},"add db","\u002Fen\u002Fcli\u002Fadd-db","en\u002F9.cli\u002F10.add-db","i-lucide-database-zap",{"title":280,"path":281,"stem":282,"icon":283},"add compose \u002F add docker","\u002Fen\u002Fcli\u002Fadd-compose","en\u002F9.cli\u002F11.add-compose","i-lucide-container",{"title":285,"path":286,"stem":287,"icon":288},"add mail","\u002Fen\u002Fcli\u002Fadd-mail","en\u002F9.cli\u002F12.add-mail","i-lucide-mail-plus",{"title":290,"path":291,"stem":292,"icon":293},"add notification","\u002Fen\u002Fcli\u002Fadd-notification","en\u002F9.cli\u002F13.add-notification","i-lucide-bell-plus",{"title":295,"path":296,"stem":297,"icon":298},"add realtime","\u002Fen\u002Fcli\u002Fadd-realtime","en\u002F9.cli\u002F14.add-realtime","i-lucide-radio-tower",{"title":300,"path":301,"stem":302,"icon":165},"add seeder","\u002Fen\u002Fcli\u002Fadd-seeder","en\u002F9.cli\u002F15.add-seeder",{"title":304,"path":305,"stem":306,"icon":307},"new project","\u002Fen\u002Fcli\u002Fnew-project","en\u002F9.cli\u002F2.new-project","i-lucide-folder-plus",{"title":309,"path":310,"stem":311,"icon":312},"new module","\u002Fen\u002Fcli\u002Fnew-module","en\u002F9.cli\u002F3.new-module","i-lucide-blocks",{"title":314,"path":315,"stem":316,"icon":317},"add surface","\u002Fen\u002Fcli\u002Fadd-surface","en\u002F9.cli\u002F4.add-surface","i-lucide-layers-2",{"title":319,"path":320,"stem":321,"icon":322},"add core","\u002Fen\u002Fcli\u002Fadd-core","en\u002F9.cli\u002F5.add-core","i-lucide-box",{"title":324,"path":325,"stem":326,"icon":327},"add handler","\u002Fen\u002Fcli\u002Fadd-handler","en\u002F9.cli\u002F6.add-handler","i-lucide-webhook",{"title":329,"path":330,"stem":331,"icon":332},"new migration","\u002Fen\u002Fcli\u002Fnew-migration","en\u002F9.cli\u002F7.new-migration","i-lucide-file-plus",{"title":334,"path":335,"stem":336,"icon":195},"worker generators","\u002Fen\u002Fcli\u002Fworker-generators","en\u002F9.cli\u002F8.worker-generators",{"title":338,"path":339,"stem":340,"icon":341},"upgrade templates","\u002Fen\u002Fcli\u002Fupgrade-templates","en\u002F9.cli\u002F9.upgrade-templates","i-lucide-refresh-cw",{"id":343,"title":314,"body":344,"description":1044,"extension":1045,"links":1046,"meta":1047,"navigation":1048,"path":315,"seo":1049,"stem":316,"__hash__":1050},"docs_en\u002Fen\u002F9.cli\u002F4.add-surface.md",{"type":345,"value":346,"toc":1037},"minimark",[347,418,439,482,488,551,556,621,652,693,701,738,817,852,874,878,896,955,982,986,1020,1033],[348,349,354],"pre",{"className":350,"code":351,"language":352,"meta":353,"style":353},"language-sh shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","go tool gpsystem add surface \u003Cmodule> \u003Csurface> [--db \u003Cname>]\n","sh","",[355,356,357],"code",{"__ignoreMap":353},[358,359,362,366,370,373,376,379,383,386,390,393,395,398,400,402,405,408,411,413,415],"span",{"class":360,"line":361},"line",1,[358,363,365],{"class":364},"sBMFI","go",[358,367,369],{"class":368},"sfazB"," tool",[358,371,372],{"class":368}," gpsystem",[358,374,375],{"class":368}," add",[358,377,378],{"class":368}," surface",[358,380,382],{"class":381},"sMK4o"," \u003C",[358,384,385],{"class":368},"modul",[358,387,389],{"class":388},"sTEyZ","e",[358,391,392],{"class":381},">",[358,394,382],{"class":381},[358,396,397],{"class":368},"surfac",[358,399,389],{"class":388},[358,401,392],{"class":381},[358,403,404],{"class":388}," [--db ",[358,406,407],{"class":381},"\u003C",[358,409,410],{"class":368},"nam",[358,412,389],{"class":388},[358,414,392],{"class":381},[358,416,417],{"class":368},"]\n",[419,420,421,422,425,426,429,430,433,434,438],"p",{},"Surfaces are parallel per-audience bundles inside one module, for example a public ",[355,423,424],{},"api"," next to an ",[355,427,428],{},"admin"," or ",[355,431,432],{},"backoffice"," surface (see ",[435,436,35],"a",{"href":437},"\u002Fen\u002Fgetting-started\u002Fproject-structure#what-can-live-inside-a-module","). The surface name is free-form (lowercase letters and digits, starting with a letter); every surface is generated from the same minimal template. Each gets its own TypeSpec service, generated package, handler and base path:",[440,441,442,455],"table",{},[443,444,445],"thead",{},[446,447,448,452],"tr",{},[449,450,451],"th",{},"Surface",[449,453,454],{},"Base path",[456,457,458,470],"tbody",{},[446,459,460,465],{},[461,462,463],"td",{},[355,464,424],{},[461,466,467],{},[355,468,469],{},"\u002Fapi\u002Fv1\u002F\u003Cmodule>",[446,471,472,477],{},[461,473,474,475],{},"any other name, e.g. ",[355,476,428],{},[461,478,479],{},[355,480,481],{},"\u002Fapi\u002Fv1\u002F\u003Csurface>\u002F\u003Cmodule>",[419,483,484,485,487],{},"The name carries no behavior: ",[355,486,424],{}," is only special in that it mounts at the module root, while every other surface is namespaced under its own prefix so sibling surfaces never collide.",[489,490,491,495,496,499,500,503,504,507,508,511,512,503,515,518,519,522,523,526,527,530,531,534,535,538,539,542,543,546,547,550],"note",{},[492,493,494],"strong",{},"Pre-rename projects."," This command used to be called ",[355,497,498],{},"add domain","; the old name still works as a hidden alias for one release. The CLI reads the old ",[355,501,502],{},"domains:","\u002F",[355,505,506],{},"domainDBs:"," keys in ",[355,509,510],{},"gpsystem.yaml"," and migrates them to ",[355,513,514],{},"surfaces:",[355,516,517],{},"surfaceDBs:"," automatically on the next save. An existing module can be moved to the new layout by hand: ",[355,520,521],{},"git mv internal\u002Fmodules\u002F\u003Cmodule>\u002F{api,web,admin} internal\u002Fmodules\u002F\u003Cmodule>\u002Fsurfaces\u002F",", then ",[355,524,525],{},"gpsystem add core \u003Cmodule>"," for the shared skeletons, updating the import paths, changing the ",[355,528,529],{},"\u002F\u002F gpsystem:domains"," anchor comment in ",[355,532,533],{},"register.go"," to ",[355,536,537],{},"\u002F\u002F gpsystem:surfaces",", and bumping the relative paths in the ",[355,540,541],{},"\u002F\u002Fgo:generate"," directives and the ",[355,544,545],{},"api\u002Foapi-codegen\u002F*.yaml"," configs one level deeper (because of the ",[355,548,549],{},"surfaces\u002F"," wrapper).",[552,553,555],"h2",{"id":554},"what-it-generates","What it generates",[419,557,558,559,562,563,566,567,570,571,574,575,578,579,582,583,586,587,503,590,593,594,597,598,601,602,605,606,611,612,617,618,620],{},"A minimal skeleton: the contract (",[355,560,561],{},"spec\u002Ftypespec\u002Fmodules\u002F\u003Cmodule>\u002F\u003Csurface>.tsp"," + ",[355,564,565],{},"models\u002F\u003Csurface>.tsp","), the oapi-codegen config (",[355,568,569],{},"api\u002Foapi-codegen\u002F\u003Cmodule>-\u003Csurface>.yaml","), a handler wired to the generated strict interface (its sample ",[355,572,573],{},"List"," calls a minimal ",[355,576,577],{},"service\u002F"," seam and wraps any error with ",[355,580,581],{},"errs.Wrap",", so surface errors carry a stack from day one), a ",[355,584,585],{},"policy\u002F"," package with a registry stub wired up to enforce ",[355,588,589],{},"@permission",[355,591,592],{},"@policy"," tags, and a kept-empty ",[355,595,596],{},"http\u002Fmapper\u002F"," directory (",[355,599,600],{},".gitkeep",") for your own code. Generated files carry no explanatory comments, only the ",[355,603,604],{},"gpsystem:*"," anchors the generator injects into. The generator wires no authentication or migrations: the kit's ",[435,607,608],{"href":204},[355,609,610],{},"auth"," and ",[435,613,614],{"href":209},[355,615,616],{},"rbac"," packages are available when you need them; see ",[435,619,213],{"href":214}," for policies.",[419,622,623,624,627,628,562,631,634,635,638,639,642,643,646,647,651],{},"Alongside a module's ",[492,625,626],{},"first"," surface, the command also stamps the module-level shared skeletons: ",[355,629,630],{},"core\u002Fcore.go",[355,632,633],{},"core\u002Ferrors.go"," (the latter with an ",[355,636,637],{},"ErrNotFound"," ",[355,640,641],{},"errs.Define"," example) and ",[355,644,645],{},"repository\u002Fdoc.go",". Later surfaces skip these and reuse the existing shared core. For older modules without a shared core, ",[435,648,649],{"href":320},[355,650,319],{}," retrofits the same skeletons.",[419,653,654,655,658,659,662,663,666,667,669,670,673,674,666,677,680,681,684,685,688,689,692],{},"Wiring happens via anchor insertions: an exported ",[355,656,657],{},"Register\u003CSurface>(router, deps)"," function (e.g. ",[355,660,661],{},"RegisterAdmin",") is appended at the file-level ",[355,664,665],{},"gpsystem:surfaces"," anchor in ",[355,668,533],{},", and the matching ",[355,671,672],{},"\u003Cmodule>.Register\u003CSurface>(api, deps)"," call is inserted at the ",[355,675,676],{},"gpsystem:registrations",[355,678,679],{},"cmd\u002F\u003Cmodule>\u002Fmain.go",". Every surface therefore lives in its own function: per-surface middleware (e.g. admin JWT: ",[355,682,683],{},"adminGroup.Use(auth.Middleware(deps.Auth), rbac.RequireRole(\"admin\"))","; see ",[435,686,687],{"href":204},"authentication",") goes inside that surface's ",[355,690,691],{},"Register\u003CSurface>"," function without touching the others.",[552,694,696,611,698,700],{"id":695},"permission-and-policy-in-the-contract",[355,697,589],{},[355,699,592],{}," in the contract",[419,702,703,704,707,708,711,712,715,716,719,720,723,724,711,727,730,731,711,734,737],{},"Operations in the generated ",[355,705,706],{},"\u003Csurface>.tsp"," can be tagged with the ",[355,709,710],{},"@permission(\"...\")"," \u002F ",[355,713,714],{},"@policy(\"...\")"," decorators: the decorator library (",[355,717,718],{},"spec\u002Ftypespec\u002Flib\u002Fpolicy.tsp",") came with the project scaffold, and ",[355,721,722],{},"main.tsp"," already imports it. The tags reach the OpenAPI spec as ",[355,725,726],{},"x-permission",[355,728,729],{},"x-policy"," extensions, and from there the generated ",[355,732,733],{},"PermissionByOperation",[355,735,736],{},"PolicyByOperation"," maps:",[348,739,743],{"className":740,"code":741,"language":742,"meta":353,"style":353},"language-tsp shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","@post\n@route(\"\u002Forders\")\n@operationId(\"PlaceOrder\")\n@permission(\"orders.place\")\n@policy(\"orders.place\")\nplaceOrder(@body body: PlaceOrderInput): Order | Unauthorized | Forbidden;\n","tsp",[355,744,745,751,766,779,791,802],{"__ignoreMap":353},[358,746,747],{"class":360,"line":361},[358,748,750],{"class":749},"swJcz","@post\n",[358,752,754,757,760,763],{"class":360,"line":753},2,[358,755,756],{"class":749},"@route",[358,758,759],{"class":381},"(",[358,761,762],{"class":368},"\"\u002Forders\"",[358,764,765],{"class":381},")\n",[358,767,769,772,774,777],{"class":360,"line":768},3,[358,770,771],{"class":749},"@operationId",[358,773,759],{"class":381},[358,775,776],{"class":368},"\"PlaceOrder\"",[358,778,765],{"class":381},[358,780,782,784,786,789],{"class":360,"line":781},4,[358,783,589],{"class":749},[358,785,759],{"class":381},[358,787,788],{"class":368},"\"orders.place\"",[358,790,765],{"class":381},[358,792,794,796,798,800],{"class":360,"line":793},5,[358,795,592],{"class":749},[358,797,759],{"class":381},[358,799,788],{"class":368},[358,801,765],{"class":381},[358,803,805,808,811,814],{"class":360,"line":804},6,[358,806,807],{"class":388},"placeOrder(",[358,809,810],{"class":749},"@body",[358,812,813],{"class":388}," body: PlaceOrderInput): Order | Unauthorized | Forbidden",[358,815,816],{"class":381},";\n",[419,818,819,820,822,823,826,827,830,831,833,834,711,837,840,841,844,845,848,849,851],{},"The enforcement side is scaffolded too: the ",[355,821,585],{}," package's registry stub (",[355,824,825],{},"policy.New()",", containing the ",[355,828,829],{},"gpsystem:policies"," anchor) is already threaded into the generated strict middleware inside ",[355,832,691],{}," (",[355,835,836],{},"policy.FiberEnforcer",[355,838,839],{},"policy.HTTPEnforcer",", per engine). Protecting an operation therefore takes two steps: a tag in the ",[355,842,843],{},".tsp"," and a ",[355,846,847],{},"reg.Register(\"...\", ...)"," call at the anchor; a tagged but unregistered policy fails closed. Full semantics: ",[435,850,213],{"href":214},".",[853,854,855,856,562,859,611,861,562,864,866,867,711,870,873],"warning",{},"Namespaces (and thus emitted spec filenames) concatenate module + surface: ",[355,857,858],{},"news",[355,860,428],{},[355,862,863],{},"newsadmin",[355,865,424],{}," both produce ",[355,868,869],{},"Shop.NewsAdmin.yaml",[355,871,872],{},"Shop.Newsadmin.yaml",", which collide on case-insensitive filesystems. Pick names that keep the concatenations distinct.",[552,875,877],{"id":876},"binding-the-repository-to-a-named-connection","Binding the repository to a named connection",[419,879,880,881,884,885,888,889,892,893,895],{},"By default the module's shared ",[355,882,883],{},"repository\u002F"," is flat, bound to the project's default database connection. ",[355,886,887],{},"--db \u003Cname>"," binds it to a ",[435,890,891],{"href":276},"named connection"," added earlier with ",[355,894,275],{},", and renders the repository skeleton as a connection-named subfolder instead:",[348,897,899],{"className":350,"code":898,"language":352,"meta":353,"style":353},"go tool gpsystem add db news analytics --connector bun\ngo tool gpsystem add surface news reports --db analytics\n# -> internal\u002Fmodules\u002Fnews\u002Frepository\u002Fanalytics\u002Fdoc.go\n",[355,900,901,926,949],{"__ignoreMap":353},[358,902,903,905,907,909,911,914,917,920,923],{"class":360,"line":361},[358,904,365],{"class":364},[358,906,369],{"class":368},[358,908,372],{"class":368},[358,910,375],{"class":368},[358,912,913],{"class":368}," db",[358,915,916],{"class":368}," news",[358,918,919],{"class":368}," analytics",[358,921,922],{"class":368}," --connector",[358,924,925],{"class":368}," bun\n",[358,927,928,930,932,934,936,938,940,943,946],{"class":360,"line":753},[358,929,365],{"class":364},[358,931,369],{"class":368},[358,933,372],{"class":368},[358,935,375],{"class":368},[358,937,378],{"class":368},[358,939,916],{"class":368},[358,941,942],{"class":368}," reports",[358,944,945],{"class":368}," --db",[358,947,948],{"class":368}," analytics\n",[358,950,951],{"class":360,"line":768},[358,952,954],{"class":953},"sHwdD","# -> internal\u002Fmodules\u002Fnews\u002Frepository\u002Fanalytics\u002Fdoc.go\n",[419,956,957,960,961,963,964,711,967,970,971,974,975,978,979,981],{},[355,958,959],{},"\u003Cname>"," must already exist on the module (",[355,962,275],{}," first); the command fails fast, listing the module's available connections, if it does not. The ",[355,965,966],{},"\u003CName>DB",[355,968,969],{},"\u003CName>Transactor"," fields the connection added to ",[355,972,973],{},"Dependencies"," are already there; only the repository directory changes. Surfaces without ",[355,976,977],{},"--db"," are unaffected and the module keeps the flat ",[355,980,883],{}," layout.",[552,983,985],{"id":984},"after-generation","After generation",[348,987,989],{"className":350,"code":988,"language":352,"meta":353,"style":353},"mise run generate\ngo run .\u002Fcmd\u002F\u003Cmodule>\n",[355,990,991,1002],{"__ignoreMap":353},[358,992,993,996,999],{"class":360,"line":361},[358,994,995],{"class":364},"mise",[358,997,998],{"class":368}," run",[358,1000,1001],{"class":368}," generate\n",[358,1003,1004,1006,1008,1011,1013,1015,1017],{"class":360,"line":753},[358,1005,365],{"class":364},[358,1007,998],{"class":368},[358,1009,1010],{"class":368}," .\u002Fcmd\u002F",[358,1012,407],{"class":381},[358,1014,385],{"class":368},[358,1016,389],{"class":388},[358,1018,1019],{"class":381},">\n",[419,1021,1022,1023,1025,1026,1029,1030,851],{},"The new surface boots and serves immediately; implement it by fleshing out the ",[355,1024,577],{}," seam (putting rules shared across surfaces into the module's ",[355,1027,1028],{},"core\u002F"," package, where every surface can call them) and returning real data: the handler already calls the service and wraps its errors with ",[355,1031,1032],{},"errs",[1034,1035,1036],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}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 .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":353,"searchDepth":753,"depth":753,"links":1038},[1039,1040,1042,1043],{"id":554,"depth":753,"text":555},{"id":695,"depth":753,"text":1041},"@permission and @policy in the contract",{"id":876,"depth":753,"text":877},{"id":984,"depth":753,"text":985},"Add another surface (parallel HTTP bundle) to an existing module, under any name.","md",null,{},{"icon":317},{"title":314,"description":1044},"EaG9IQ8HGMJ2PMEa8m8khPQgeHs65-5O7TeT3erQH4A",[1052,1054],{"title":309,"path":310,"stem":311,"description":1053,"icon":312,"children":-1},"Generate a module (entry point, first surface, contract) into the current project.",{"title":319,"path":320,"stem":321,"description":1055,"icon":322,"children":-1},"Retrofit an existing module with the shared core\u002F and module-level repository\u002F skeletons.",1784668713562]