[{"data":1,"prerenderedAt":1416},["ShallowReactive",2],{"docs-nav:en":3,"docs:\u002Fdocs\u002Finfra\u002Fgradient":186},[4],{"title":5,"path":6,"stem":7,"children":8,"page":-1},"Docs","\u002Fdocs","docs",[9,12,16,20,24,28,50,75,120,145],{"title":10,"path":6,"stem":11},"What is Fougere","docs\u002Findex",{"title":13,"path":14,"stem":15},"Getting started","\u002Fdocs\u002Fgetting-started","docs\u002F01.getting-started",{"title":17,"path":18,"stem":19},"An app you already have","\u002Fdocs\u002Fexisting-app","docs\u002F02.existing-app",{"title":21,"path":22,"stem":23},"Bring your schema","\u002Fdocs\u002Fbring-your-schema","docs\u002F03.bring-your-schema",{"title":25,"path":26,"stem":27},"The CLI","\u002Fdocs\u002Fcli","docs\u002F04.cli",{"title":29,"path":30,"stem":31,"children":32,"page":49},"Concepts","\u002Fdocs\u002Fconcepts","docs\u002F2.concepts",[33,37,41,45],{"title":34,"path":35,"stem":36},"Philosophy","\u002Fdocs\u002Fconcepts\u002Fphilosophy","docs\u002F2.concepts\u002F1.philosophy",{"title":38,"path":39,"stem":40},"The Frond","\u002Fdocs\u002Fconcepts\u002Ffrond","docs\u002F2.concepts\u002F2.frond",{"title":42,"path":43,"stem":44},"The shortest path","\u002Fdocs\u002Fconcepts\u002Fshortest-path","docs\u002F2.concepts\u002F3.shortest-path",{"title":46,"path":47,"stem":48},"The base","\u002Fdocs\u002Fconcepts\u002Fthe-base","docs\u002F2.concepts\u002F4.the-base",false,{"title":51,"path":52,"stem":53,"children":54,"page":49},"Schema","\u002Fdocs\u002Fschema","docs\u002F3.schema",[55,59,63,67,71],{"title":56,"path":57,"stem":58},"Entities","\u002Fdocs\u002Fschema\u002Fentities","docs\u002F3.schema\u002F1.entities",{"title":60,"path":61,"stem":62},"Views","\u002Fdocs\u002Fschema\u002Fviews","docs\u002F3.schema\u002F2.views",{"title":64,"path":65,"stem":66},"The identity card","\u002Fdocs\u002Fschema\u002Fcard","docs\u002F3.schema\u002F3.card",{"title":68,"path":69,"stem":70},"Standard Schema","\u002Fdocs\u002Fschema\u002Fstandard-schema","docs\u002F3.schema\u002F4.standard-schema",{"title":72,"path":73,"stem":74},"How a schema moves","\u002Fdocs\u002Fschema\u002Fevolution","docs\u002F3.schema\u002F5.evolution",{"title":76,"path":77,"stem":78,"children":79,"page":49},"Business","\u002Fdocs\u002Fbusiness","docs\u002F4.business",[80,84,88,92,96,100,104,108,112,116],{"title":81,"path":82,"stem":83},"Handlers","\u002Fdocs\u002Fbusiness\u002Fhandlers","docs\u002F4.business\u002F1.handlers",{"title":85,"path":86,"stem":87},"Ports","\u002Fdocs\u002Fbusiness\u002Fports","docs\u002F4.business\u002F10.ports",{"title":89,"path":90,"stem":91},"Presenters","\u002Fdocs\u002Fbusiness\u002Fpresenters","docs\u002F4.business\u002F2.presenters",{"title":93,"path":94,"stem":95},"Collectors","\u002Fdocs\u002Fbusiness\u002Fcollectors","docs\u002F4.business\u002F3.collectors",{"title":97,"path":98,"stem":99},"Errors","\u002Fdocs\u002Fbusiness\u002Ferrors","docs\u002F4.business\u002F4.errors",{"title":101,"path":102,"stem":103},"Seeds","\u002Fdocs\u002Fbusiness\u002Fseeds","docs\u002F4.business\u002F5.seeds",{"title":105,"path":106,"stem":107},"Facts","\u002Fdocs\u002Fbusiness\u002Ffacts","docs\u002F4.business\u002F6.facts",{"title":109,"path":110,"stem":111},"Storage","\u002Fdocs\u002Fbusiness\u002Fstorage","docs\u002F4.business\u002F7.storage",{"title":113,"path":114,"stem":115},"Writes that stand or fall as one","\u002Fdocs\u002Fbusiness\u002Ftogether","docs\u002F4.business\u002F8.together",{"title":117,"path":118,"stem":119},"Repositories","\u002Fdocs\u002Fbusiness\u002Frepositories","docs\u002F4.business\u002F9.repositories",{"title":121,"path":122,"stem":123,"children":124,"page":49},"Client","\u002Fdocs\u002Fclient","docs\u002F5.client",[125,129,133,137,141],{"title":126,"path":127,"stem":128},"Queries & commands","\u002Fdocs\u002Fclient\u002Fqueries-commands","docs\u002F5.client\u002F1.queries-commands",{"title":130,"path":131,"stem":132},"Forms","\u002Fdocs\u002Fclient\u002Fforms","docs\u002F5.client\u002F2.forms",{"title":134,"path":135,"stem":136},"Session","\u002Fdocs\u002Fclient\u002Fsession","docs\u002F5.client\u002F3.session",{"title":138,"path":139,"stem":140},"invoke","\u002Fdocs\u002Fclient\u002Finvoke","docs\u002F5.client\u002F4.invoke",{"title":142,"path":143,"stem":144},"Back-office","\u002Fdocs\u002Fclient\u002Fadmin","docs\u002F5.client\u002F5.admin",{"title":146,"path":147,"stem":148,"children":149,"page":49},"Infrastructure","\u002Fdocs\u002Finfra","docs\u002F6.infra",[150,154,158,162,166,170,174,178,182],{"title":151,"path":152,"stem":153},"The gradient","\u002Fdocs\u002Finfra\u002Fgradient","docs\u002F6.infra\u002F1.gradient",{"title":155,"path":156,"stem":157},"Surfaces — REST & GraphQL","\u002Fdocs\u002Finfra\u002Fsurfaces","docs\u002F6.infra\u002F2.surfaces",{"title":159,"path":160,"stem":161},"Deployment","\u002Fdocs\u002Finfra\u002Fdeployment","docs\u002F6.infra\u002F3.deployment",{"title":163,"path":164,"stem":165},"Hosts","\u002Fdocs\u002Finfra\u002Fhosts","docs\u002F6.infra\u002F4.hosts",{"title":167,"path":168,"stem":169},"Sources","\u002Fdocs\u002Finfra\u002Fsources","docs\u002F6.infra\u002F5.sources",{"title":171,"path":172,"stem":173},"Lifecycle","\u002Fdocs\u002Finfra\u002Flifecycle","docs\u002F6.infra\u002F6.lifecycle",{"title":175,"path":176,"stem":177},"Identity across Fronds","\u002Fdocs\u002Finfra\u002Fidentity","docs\u002F6.infra\u002F7.identity",{"title":179,"path":180,"stem":181},"Observability","\u002Fdocs\u002Finfra\u002Fobservability","docs\u002F6.infra\u002F8.observability",{"title":183,"path":184,"stem":185},"Testing","\u002Fdocs\u002Finfra\u002Ftesting","docs\u002F6.infra\u002F9.testing",{"id":187,"title":151,"body":188,"description":1411,"extension":1412,"meta":1413,"navigation":719,"path":152,"seo":1414,"stem":153,"__hash__":1415},"docs_en\u002Fdocs\u002F6.infra\u002F1.gradient.md",{"type":189,"value":190,"toc":1399},"minimark",[191,195,199,204,217,356,364,368,452,459,488,492,502,521,526,530,536,973,988,998,1002,1009,1094,1101,1107,1111,1209,1213,1219,1222,1226,1242,1245,1299,1307,1311,1324,1330,1336,1365,1368,1378,1382,1388,1395],[192,193,151],"h1",{"id":194},"the-gradient",[196,197,198],"p",{},"The gradient is not a feature the framework adds. It is what follows from a Frond naming\nno address: since nothing in it says which process it runs in, moving it to another one\nchanges no business code and no page that calls it.",[200,201,203],"h2",{"id":202},"five-rungs-and-the-price-of-each","Five rungs, and the price of each",[196,205,206,207,211,212,216],{},"Fougere is progressive in one precise sense, and it is the one you can check: ",[208,209,210],"strong",{},"going up a\nrung never rewrites what you wrote."," What a rung ",[213,214,215],"em",{},"does"," cost is stated here rather than\nleft for you to discover.",[218,219,220,238],"table",{},[221,222,223],"thead",{},[224,225,226,229,232,235],"tr",{},[227,228],"th",{},[227,230,231],{},"The step",[227,233,234],{},"What it buys",[227,236,237],{},"What it costs",[239,240,241,256,287,311,338],"tbody",{},[224,242,243,247,250,253],{},[244,245,246],"td",{},"0",[244,248,249],{},"one Frond, one process",[244,251,252],{},"a running app — no network, no HTTP required",[244,254,255],{},"nothing",[224,257,258,261,268,271],{},[244,259,260],{},"1",[244,262,263,264],{},"a second domain, in ",[265,266,267],"code",{},"fronds\u002Fbilling\u002F",[244,269,270],{},"an ownership boundary, before any network",[244,272,273,274,277,278,281,282,286],{},"no reaching into another Frond's files (",[265,275,276],{},"fougere check"," reports ",[265,279,280],{},"cross-frond-import","), and a ",[283,284,285],"a",{"href":94},"collector"," has to live in the Frond that consumes it — one that does not binds the request body to the parameter that wanted a user",[224,288,289,292,297,300],{},[244,290,291],{},"2",[244,293,294],{},[265,295,296],{},"remotes: { blog: '…' }",[244,298,299],{},"the Frond runs elsewhere, same code",[244,301,302,303,306,307,310],{},"the hop and its JSON; beyond loopback the receiver must be able to ",[283,304,305],{"href":176},"establish its caller",", which is two commands at deployment; and two capabilities go missing — a ",[283,308,309],{"href":156},"named surface"," answers nothing for a remote Frond, and one address per Frond means the same Frond cannot be deployed twice",[224,312,313,316,319,322],{},[244,314,315],{},"3",[244,317,318],{},"the Frond moves to its own repo",[244,320,321],{},"its own team, release cycle, deployment",[244,323,324,325,328,329,332,333,337],{},"the scan no longer reads it: the ",[208,326,327],{},"contract"," must be copied (",[265,330,331],{},"fougere sync","), the ",[283,334,336],{"href":335},"\u002Fdocs\u002Fbusiness\u002Ffacts#across-repositories","carrier"," under fact dispatch is yours to write, and a fact is judged strictly — so its readers deploy before its sender",[224,339,340,343,346,349],{},[244,341,342],{},"4",[244,344,345],{},"the Frond is not TypeScript",[244,347,348],{},"a Rust or Python host answering the same calls",[244,350,351,352,355],{},"you honour ",[283,353,354],{"href":65},"the card"," and the wire yourself",[196,357,358,359,363],{},"Rung 0 costs nothing, and it is where every demo in this repository sits. Rung 1 is reversible — the root Frond does not move when the second\ndomain arrives — but it is not free: it is where a misplaced collector becomes a question\nabout privilege rather than about layout. Rung 2 buys the hop and pays two capabilities for\nit. Rung 3 is the real border, because it removes the colocation the scan was reading: see\n",[283,360,362],{"href":361},"#where-the-code-lives","where the code lives",".",[200,365,367],{"id":366},"the-topology-statement","The topology statement",[369,370,375],"pre",{"className":371,"code":372,"language":373,"meta":374,"style":374},"language-ts shiki shiki-themes material-theme-lighter github-light github-dark","\u002F\u002F fougere.config.ts\nexport default defineFougere({\n  remotes: { blog: 'http:\u002F\u002F127.0.0.1:4100' },\n});\n","ts","",[265,376,377,386,408,440],{"__ignoreMap":374},[378,379,382],"span",{"class":380,"line":381},"line",1,[378,383,385],{"class":384},"sutJx","\u002F\u002F fougere.config.ts\n",[378,387,389,393,396,400,404],{"class":380,"line":388},2,[378,390,392],{"class":391},"sVHd0","export",[378,394,395],{"class":391}," default",[378,397,399],{"class":398},"sGLFI"," defineFougere",[378,401,403],{"class":402},"su5hD","(",[378,405,407],{"class":406},"sP7_E","{\n",[378,409,411,415,418,421,424,426,430,434,437],{"class":380,"line":410},3,[378,412,414],{"class":413},"skxfh","  remotes",[378,416,417],{"class":406},":",[378,419,420],{"class":406}," {",[378,422,423],{"class":413}," blog",[378,425,417],{"class":406},[378,427,429],{"class":428},"sjJ54"," '",[378,431,433],{"class":432},"s_sjI","http:\u002F\u002F127.0.0.1:4100",[378,435,436],{"class":428},"'",[378,438,439],{"class":406}," },\n",[378,441,443,446,449],{"class":380,"line":442},4,[378,444,445],{"class":406},"}",[378,447,448],{"class":402},")",[378,450,451],{"class":406},";\n",[196,453,454,455,458],{},"A Frond declared in ",[265,456,457],{},"remotes"," is still scanned: its entities continue to provide metadata\nfor forms, validation, and DI. Its operations instead run at the remote address. Removing\nthe line restores local execution. The multi-Frond demo covers both configurations,\nincluding a production build:",[369,460,464],{"className":461,"code":462,"language":463,"meta":374,"style":374},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","pnpm dev:blog      # the blog Frond alone, in its own process (:4100)\npnpm dev           # the app — consumes it through the remotes line\n","bash",[265,465,466,478],{"__ignoreMap":374},[378,467,468,472,475],{"class":380,"line":381},[378,469,471],{"class":470},"sbgvK","pnpm",[378,473,474],{"class":432}," dev:blog",[378,476,477],{"class":384},"      # the blog Frond alone, in its own process (:4100)\n",[378,479,480,482,485],{"class":380,"line":388},[378,481,471],{"class":470},[378,483,484],{"class":432}," dev",[378,486,487],{"class":384},"           # the app — consumes it through the remotes line\n",[200,489,491],{"id":490},"the-call-contract","The call contract",[196,493,494,495,498,499,363],{},"A call is a value: ",[265,496,497],{},"(entity, operation, invocation)"," with\n",[265,500,501],{},"invocation = { params, query, body, state }",[503,504,505,512,518],"ul",{},[506,507,508,511],"li",{},[265,509,510],{},"createLocalRunner"," executes strictly locally;",[506,513,514,517],{},[265,515,516],{},"createAppRunner"," follows the topology — local façades, remote doublures;",[506,519,520],{},"transports serialize this value without changing its structure.",[522,523],"d2-figure",{"caption":524,"name":525},"One call, both topologies. The hop is inserted; the façade that judges, binds, presents and projects is the same one on either side.","call-path",[200,527,529],{"id":528},"the-wire-format","The wire format",[196,531,532,533,417],{},"Process-to-process is JSON-RPC 2.0 on ",[265,534,535],{},"POST \u002F_fougere\u002Fcall",[369,537,541],{"className":538,"code":539,"language":540,"meta":374,"style":374},"language-jsonc shiki shiki-themes material-theme-lighter github-light github-dark","\u002F\u002F → request\n{ \"jsonrpc\": \"2.0\", \"id\": 1, \"method\": \"post.publish\",\n  \"params\": { \"params\": { \"id\": \"…\" }, \"query\": {}, \"body\": null, \"state\": { \"user\": { … } } } }\n\n\u002F\u002F ← success\n{ \"jsonrpc\": \"2.0\", \"id\": 1, \"result\": { \"id\": \"…\", \"status\": \"published\", … } }\n\n\u002F\u002F ← domain failure — revived as FougereError on the calling side\n{ \"jsonrpc\": \"2.0\", \"id\": 1, \"error\": { \"code\": -32000,\n    \"data\": { \"code\": \"CONFLICT\", \"message\": \"Already published\",\n              \"entity\": \"post\", \"operation\": \"publish\" } } }\n","jsonc",[265,542,543,548,610,715,721,727,811,816,822,879,929],{"__ignoreMap":374},[378,544,545],{"class":380,"line":381},[378,546,547],{"class":384},"\u002F\u002F → request\n",[378,549,550,553,557,561,564,566,568,571,573,576,578,581,583,585,589,591,593,596,598,600,602,605,607],{"class":380,"line":388},[378,551,552],{"class":406},"{",[378,554,556],{"class":555},"s39Yj"," \"",[378,558,560],{"class":559},"sseR_","jsonrpc",[378,562,563],{"class":555},"\"",[378,565,417],{"class":406},[378,567,556],{"class":428},[378,569,570],{"class":432},"2.0",[378,572,563],{"class":428},[378,574,575],{"class":406},",",[378,577,556],{"class":555},[378,579,580],{"class":559},"id",[378,582,563],{"class":555},[378,584,417],{"class":406},[378,586,588],{"class":587},"srdBf"," 1",[378,590,575],{"class":406},[378,592,556],{"class":555},[378,594,595],{"class":559},"method",[378,597,563],{"class":555},[378,599,417],{"class":406},[378,601,556],{"class":428},[378,603,604],{"class":432},"post.publish",[378,606,563],{"class":428},[378,608,609],{"class":406},",\n",[378,611,612,615,618,620,622,624,626,629,631,633,635,637,639,641,643,645,648,650,653,655,658,660,662,665,667,670,672,674,677,679,681,684,686,688,690,692,695,697,699,701,705,708,710,712],{"class":380,"line":410},[378,613,614],{"class":555},"  \"",[378,616,617],{"class":559},"params",[378,619,563],{"class":555},[378,621,417],{"class":406},[378,623,420],{"class":406},[378,625,556],{"class":555},[378,627,617],{"class":628},"sZMiF",[378,630,563],{"class":555},[378,632,417],{"class":406},[378,634,420],{"class":406},[378,636,556],{"class":555},[378,638,580],{"class":587},[378,640,563],{"class":555},[378,642,417],{"class":406},[378,644,556],{"class":428},[378,646,647],{"class":432},"…",[378,649,563],{"class":428},[378,651,652],{"class":406}," },",[378,654,556],{"class":555},[378,656,657],{"class":628},"query",[378,659,563],{"class":555},[378,661,417],{"class":406},[378,663,664],{"class":406}," {},",[378,666,556],{"class":555},[378,668,669],{"class":628},"body",[378,671,563],{"class":555},[378,673,417],{"class":406},[378,675,676],{"class":555}," null",[378,678,575],{"class":406},[378,680,556],{"class":555},[378,682,683],{"class":628},"state",[378,685,563],{"class":555},[378,687,417],{"class":406},[378,689,420],{"class":406},[378,691,556],{"class":555},[378,693,694],{"class":587},"user",[378,696,563],{"class":555},[378,698,417],{"class":406},[378,700,420],{"class":406},[378,702,704],{"class":703},"srjyR"," …",[378,706,707],{"class":406}," }",[378,709,707],{"class":406},[378,711,707],{"class":406},[378,713,714],{"class":406}," }\n",[378,716,717],{"class":380,"line":442},[378,718,720],{"emptyLinePlaceholder":719},true,"\n",[378,722,724],{"class":380,"line":723},5,[378,725,726],{"class":384},"\u002F\u002F ← success\n",[378,728,730,732,734,736,738,740,742,744,746,748,750,752,754,756,758,760,762,765,767,769,771,773,775,777,779,781,783,785,787,789,792,794,796,798,801,803,805,807,809],{"class":380,"line":729},6,[378,731,552],{"class":406},[378,733,556],{"class":555},[378,735,560],{"class":559},[378,737,563],{"class":555},[378,739,417],{"class":406},[378,741,556],{"class":428},[378,743,570],{"class":432},[378,745,563],{"class":428},[378,747,575],{"class":406},[378,749,556],{"class":555},[378,751,580],{"class":559},[378,753,563],{"class":555},[378,755,417],{"class":406},[378,757,588],{"class":587},[378,759,575],{"class":406},[378,761,556],{"class":555},[378,763,764],{"class":559},"result",[378,766,563],{"class":555},[378,768,417],{"class":406},[378,770,420],{"class":406},[378,772,556],{"class":555},[378,774,580],{"class":628},[378,776,563],{"class":555},[378,778,417],{"class":406},[378,780,556],{"class":428},[378,782,647],{"class":432},[378,784,563],{"class":428},[378,786,575],{"class":406},[378,788,556],{"class":555},[378,790,791],{"class":628},"status",[378,793,563],{"class":555},[378,795,417],{"class":406},[378,797,556],{"class":428},[378,799,800],{"class":432},"published",[378,802,563],{"class":428},[378,804,575],{"class":406},[378,806,704],{"class":703},[378,808,707],{"class":406},[378,810,714],{"class":406},[378,812,814],{"class":380,"line":813},7,[378,815,720],{"emptyLinePlaceholder":719},[378,817,819],{"class":380,"line":818},8,[378,820,821],{"class":384},"\u002F\u002F ← domain failure — revived as FougereError on the calling side\n",[378,823,825,827,829,831,833,835,837,839,841,843,845,847,849,851,853,855,857,860,862,864,866,868,870,872,874,877],{"class":380,"line":824},9,[378,826,552],{"class":406},[378,828,556],{"class":555},[378,830,560],{"class":559},[378,832,563],{"class":555},[378,834,417],{"class":406},[378,836,556],{"class":428},[378,838,570],{"class":432},[378,840,563],{"class":428},[378,842,575],{"class":406},[378,844,556],{"class":555},[378,846,580],{"class":559},[378,848,563],{"class":555},[378,850,417],{"class":406},[378,852,588],{"class":587},[378,854,575],{"class":406},[378,856,556],{"class":555},[378,858,859],{"class":559},"error",[378,861,563],{"class":555},[378,863,417],{"class":406},[378,865,420],{"class":406},[378,867,556],{"class":555},[378,869,265],{"class":628},[378,871,563],{"class":555},[378,873,417],{"class":406},[378,875,876],{"class":587}," -32000",[378,878,609],{"class":406},[378,880,882,885,888,890,892,894,896,898,900,902,904,907,909,911,913,916,918,920,922,925,927],{"class":380,"line":881},10,[378,883,884],{"class":555},"    \"",[378,886,887],{"class":628},"data",[378,889,563],{"class":555},[378,891,417],{"class":406},[378,893,420],{"class":406},[378,895,556],{"class":555},[378,897,265],{"class":587},[378,899,563],{"class":555},[378,901,417],{"class":406},[378,903,556],{"class":428},[378,905,906],{"class":432},"CONFLICT",[378,908,563],{"class":428},[378,910,575],{"class":406},[378,912,556],{"class":555},[378,914,915],{"class":587},"message",[378,917,563],{"class":555},[378,919,417],{"class":406},[378,921,556],{"class":428},[378,923,924],{"class":432},"Already published",[378,926,563],{"class":428},[378,928,609],{"class":406},[378,930,932,935,938,940,942,944,947,949,951,953,956,958,960,962,965,967,969,971],{"class":380,"line":931},11,[378,933,934],{"class":555},"              \"",[378,936,937],{"class":587},"entity",[378,939,563],{"class":555},[378,941,417],{"class":406},[378,943,556],{"class":428},[378,945,946],{"class":432},"post",[378,948,563],{"class":428},[378,950,575],{"class":406},[378,952,556],{"class":555},[378,954,955],{"class":587},"operation",[378,957,563],{"class":555},[378,959,417],{"class":406},[378,961,556],{"class":428},[378,963,964],{"class":432},"publish",[378,966,563],{"class":428},[378,968,707],{"class":406},[378,970,707],{"class":406},[378,972,714],{"class":406},[196,974,975,977,978,981,982,984,985,987],{},[265,976,595],{}," is ",[265,979,980],{},"entity.op"," and ",[265,983,617],{}," contains the invocation. The browser sends the same\nframe to Nitro, but its ",[265,986,683],{}," is ignored and rebuilt server-side.",[196,989,990,991,994,995,997],{},"The other half of the contract is what a host answers to ",[265,992,993],{},"rpc.discover",": see\n",[283,996,64],{"href":65},", which specifies the document and what to honour to\nwrite a Frond in another language.",[200,999,1001],{"id":1000},"loopback-or-signed","Loopback or signed",[196,1003,1004,1005,1008],{},"The receiver binds ",[265,1006,1007],{},"127.0.0.1"," and caps bodies at 1 MiB. Widening it is one line, and it\ncomes with a condition:",[369,1010,1012],{"className":371,"code":1011,"language":373,"meta":374,"style":374},"await serve(runner, { hosts: ['0.0.0.0'] })   \u002F\u002F refuses to start on its own\nawait serve(runner, { hosts: ['0.0.0.0'], verify })   \u002F\u002F establishes its caller\n",[265,1013,1014,1055],{"__ignoreMap":374},[378,1015,1016,1019,1022,1025,1027,1029,1032,1034,1037,1039,1042,1044,1047,1049,1052],{"class":380,"line":381},[378,1017,1018],{"class":391},"await",[378,1020,1021],{"class":398}," serve",[378,1023,1024],{"class":402},"(runner",[378,1026,575],{"class":406},[378,1028,420],{"class":406},[378,1030,1031],{"class":413}," hosts",[378,1033,417],{"class":406},[378,1035,1036],{"class":402}," [",[378,1038,436],{"class":428},[378,1040,1041],{"class":432},"0.0.0.0",[378,1043,436],{"class":428},[378,1045,1046],{"class":402},"] ",[378,1048,445],{"class":406},[378,1050,1051],{"class":402},")   ",[378,1053,1054],{"class":384},"\u002F\u002F refuses to start on its own\n",[378,1056,1057,1059,1061,1063,1065,1067,1069,1071,1073,1075,1077,1079,1082,1084,1087,1089,1091],{"class":380,"line":388},[378,1058,1018],{"class":391},[378,1060,1021],{"class":398},[378,1062,1024],{"class":402},[378,1064,575],{"class":406},[378,1066,420],{"class":406},[378,1068,1031],{"class":413},[378,1070,417],{"class":406},[378,1072,1036],{"class":402},[378,1074,436],{"class":428},[378,1076,1041],{"class":432},[378,1078,436],{"class":428},[378,1080,1081],{"class":402},"]",[378,1083,575],{"class":406},[378,1085,1086],{"class":402}," verify ",[378,1088,445],{"class":406},[378,1090,1051],{"class":402},[378,1092,1093],{"class":384},"\u002F\u002F establishes its caller\n",[196,1095,1096,1097,1100],{},"A receiver reachable from outside this machine, with no way to establish who is calling,\nwould believe whatever state it is handed — so it does not start. The address already\ncarries the decision: on loopback the set of callers is \"this machine\", and widening\n",[265,1098,1099],{},"hosts"," is where you write down that it is no longer.",[196,1102,1103,1104,1106],{},"What closes it is a signed envelope, verified against one public key.\n",[283,1105,175],{"href":176}," is that page.",[200,1108,1110],{"id":1109},"behaviour-after-a-split","Behaviour after a split",[218,1112,1113,1123],{},[221,1114,1115],{},[224,1116,1117,1120],{},[227,1118,1119],{},"Element",[227,1121,1122],{},"Behaviour",[239,1124,1125,1133,1155,1171,1178,1189,1201],{},[224,1126,1127,1130],{},[244,1128,1129],{},"results",[244,1131,1132],{},"identical shapes both sides, list results included",[224,1134,1135,1138],{},[244,1136,1137],{},"errors",[244,1139,1140,1141,1144,1145,1147,1148,1150,1151,1154],{},"same ",[265,1142,1143],{},"FougereError"," with ",[265,1146,265],{},", ",[265,1149,915],{},", and ",[265,1152,1153],{},"details",", rebuilt on arrival",[224,1156,1157,1160],{},[244,1158,1159],{},"optional input",[244,1161,1162,1163,1166,1167,1170],{},"absence is ",[265,1164,1165],{},"undefined","; JSON omits the property; explicit ",[265,1168,1169],{},"null"," is preserved",[224,1172,1173,1175],{},[244,1174,683],{},[244,1176,1177],{},"the consuming app's session reaches remote collectors, carried in the signed envelope",[224,1179,1180,1183],{},[244,1181,1182],{},"caller",[244,1184,1185,1188],{},[265,1186,1187],{},"invocation.caller"," names the Frond that signed — absent when nothing was established",[224,1190,1191,1194],{},[244,1192,1193],{},"unreachable host",[244,1195,1196,1197,1200],{},"typed ",[265,1198,1199],{},"SERVICE_UNAVAILABLE"," → status 503; calls resume after the host restarts",[224,1202,1203,1206],{},[244,1204,1205],{},"transport",[244,1207,1208],{},"timeout and retry on the envelope, without automatic command retries",[200,1210,1212],{"id":1211},"the-price-of-the-split","The price of the split",[196,1214,1215,1216,1218],{},"The split adds an HTTP hop and the JSON encoding and decoding it entails. A call remains\na value ",[265,1217,497],{},"; the transport frames it and unframes it, then\nthe receiving side runs the same façade as the local path. The cost therefore depends on\nthe network, payload size and runtime: Fougere does not claim to make it zero.",[196,1220,1221],{},"No figure is quoted here: this repository ships no benchmark harness, and a number you\ncannot re-run is not a measurement.",[200,1223,1225],{"id":1224},"where-the-code-lives","Where the code lives",[196,1227,1228,1229,1231,1232,1234,1235,1237,1238,1241],{},"A Frond can also move to a separate repository and remain available through ",[265,1230,457],{},".\nThree things are called \"together\" and only one is mandatory: the ",[208,1233,327],{}," must\ntravel, the ",[208,1236,265],{}," need not, the ",[208,1239,1240],{},"ports"," must reach each other.",[196,1243,1244],{},"Two commands cover the contract, depending on what you want to share:",[218,1246,1247,1259],{},[221,1248,1249],{},[224,1250,1251,1253,1256],{},[227,1252],{},[227,1254,1255],{},"What it does",[227,1257,1258],{},"When",[239,1260,1261,1279],{},[224,1262,1263,1270,1276],{},[244,1264,1265],{},[283,1266,1268],{"href":1267},"\u002Fdocs\u002Fcli#fougere-sync--consume-a-remote-frond",[265,1269,331],{},[244,1271,1272,1273,1275],{},"asks the host for ",[265,1274,993],{}," and rebuilds its entities locally",[244,1277,1278],{},"the host is running; also the only way in for a Frond written in another language",[224,1280,1281,1289,1296],{},[244,1282,1283],{},[283,1284,1286],{"href":1285},"\u002Fdocs\u002Fcli#fougere-build-frond--publish-a-fronds-entities",[265,1287,1288],{},"fougere build-frond",[244,1290,1291,1292,1295],{},"compiles ",[265,1293,1294],{},"entities\u002F**"," into an installable package",[244,1297,1298],{},"you publish the contract as a dependency",[196,1300,1301,1302,994,1304,363],{},"The Frond's source code crosses in neither case. What stays open is the ",[208,1303,1240],{},[283,1305,159],{"href":1306},"\u002Fdocs\u002Finfra\u002Fdeployment#splitting-in-production",[200,1308,1310],{"id":1309},"one-recipient-and-only-one","One recipient, and only one",[196,1312,1313,1315,1316,1319,1320,1323],{},[265,1314,457],{}," names an address per Frond, and ",[265,1317,1318],{},"Facade\u003CT>"," resolves to exactly one façade —\n",[265,1321,1322],{},"facadeKeyOf"," produces one key, the container returns one object. Every call in Fougere\nhas a single recipient, by construction.",[196,1325,1326,1327,1329],{},"That covers more than it sounds like. A device behind NAT that cannot be called still\ndeclares ",[265,1328,457],{}," and sends to a gateway it names: one recipient, and the fact that the\ndevice must open the connection is a deployment property, not a different kind of call.",[196,1331,1332,1333,417],{},"What is not covered is ",[208,1334,1335],{},"several recipients for one send",[218,1337,1338,1347],{},[221,1339,1340],{},[224,1341,1342,1344],{},[227,1343],{},[227,1345,1346],{},"who determines the recipients",[239,1348,1349,1357],{},[224,1350,1351,1354],{},[244,1352,1353],{},"a fleet",[244,1355,1356],{},"the sender names the set",[224,1358,1359,1362],{},[244,1360,1361],{},"a fact — a post was published",[244,1363,1364],{},"nobody: whoever declared an interest",[196,1366,1367],{},"Both need the same machinery — fan-out, a channel to members that carry no address, and a\nverdict when three of five succeed. Fougere has none of it. So a post being published,\nwith search reindexing and the newsletter queueing, is written today by naming both at the\nemitting site: the emitter carries the list of everything that follows from its own\naction, and a third reader reopens it.",[196,1369,1370,1371,1373,1374,1377],{},"This section exists to name the half that is here. ",[265,1372,457],{}," reads like ",[213,1375,1376],{},"the"," topology\nstatement and it is one of two; the other is designed and not shipped. Saying so keeps the\ngap visible rather than surprising.",[200,1379,1381],{"id":1380},"limits","Limits",[196,1383,1384,1385,1387],{},"Moving a Frond to a remote process adds network latency and failure modes. Fougere exposes\nthem as typed errors but does not hide them. Local mode remains the reference mode;\n",[265,1386,457],{}," changes the topology when the need arises.",[196,1389,1390,1391,1394],{},"Next: ",[283,1392,1393],{"href":156},"Surfaces"," — the same operations behind REST and GraphQL.",[1396,1397,1398],"style",{},"html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sseR_, html code.shiki .sseR_{--shiki-light:#9C3EDA;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .srjyR, html code.shiki .srjyR{--shiki-light:#90A4AE;--shiki-light-font-style:inherit;--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"title":374,"searchDepth":388,"depth":388,"links":1400},[1401,1402,1403,1404,1405,1406,1407,1408,1409,1410],{"id":202,"depth":388,"text":203},{"id":366,"depth":388,"text":367},{"id":490,"depth":388,"text":491},{"id":528,"depth":388,"text":529},{"id":1000,"depth":388,"text":1001},{"id":1109,"depth":388,"text":1110},{"id":1211,"depth":388,"text":1212},{"id":1224,"depth":388,"text":1225},{"id":1309,"depth":388,"text":1310},{"id":1380,"depth":388,"text":1381},"Configure a local or remote Frond, the protocol it uses, and the network cost.","md",{},{"title":151,"description":1411},"vN8TGmLJg8uDiid5b1BrUY-aR8kpxgv7yk2o8X3VQOk",1788560229821]