[{"data":1,"prerenderedAt":703},["ShallowReactive",2],{"docs-nav-pkg":3,"docs-page-pkg-\u002Fpkg\u002Fapi":133},[4,8,12,15,18,21,24,27,30,33,36,39,42,45,48,51,54,57,60,63,66,69,72,75,78,81,84,87,90,93,96,99,102,105,108,111,114,117,121,124,127,130],{"path":5,"title":6,"section":7},"\u002Fpkg","Overview","Start",{"path":9,"title":10,"section":11},"\u002Fpkg\u002Faccordion","Accordion","Components",{"path":13,"title":14,"section":11},"\u002Fpkg\u002Falert-dialog","Alert Dialog",{"path":16,"title":17,"section":11},"\u002Fpkg\u002Fautocomplete","Autocomplete",{"path":19,"title":20,"section":11},"\u002Fpkg\u002Favatar","Avatar",{"path":22,"title":23,"section":11},"\u002Fpkg\u002Fbutton","Button",{"path":25,"title":26,"section":11},"\u002Fpkg\u002Fcheckbox","Checkbox",{"path":28,"title":29,"section":11},"\u002Fpkg\u002Fcheckbox-group","Checkbox Group",{"path":31,"title":32,"section":11},"\u002Fpkg\u002Fcollapsible","Collapsible",{"path":34,"title":35,"section":11},"\u002Fpkg\u002Fcombobox","Combobox",{"path":37,"title":38,"section":11},"\u002Fpkg\u002Fcontext-menu","Context Menu",{"path":40,"title":41,"section":11},"\u002Fpkg\u002Fdialog","Dialog",{"path":43,"title":44,"section":11},"\u002Fpkg\u002Fdrawer","Drawer",{"path":46,"title":47,"section":11},"\u002Fpkg\u002Ffield","Field",{"path":49,"title":50,"section":11},"\u002Fpkg\u002Ffieldset","Fieldset",{"path":52,"title":53,"section":11},"\u002Fpkg\u002Fform","Form",{"path":55,"title":56,"section":11},"\u002Fpkg\u002Ficon-picker","Icon picker",{"path":58,"title":59,"section":11},"\u002Fpkg\u002Finput","Input",{"path":61,"title":62,"section":11},"\u002Fpkg\u002Fmenu","Menu",{"path":64,"title":65,"section":11},"\u002Fpkg\u002Fmenubar","Menubar",{"path":67,"title":68,"section":11},"\u002Fpkg\u002Fmeter","Meter",{"path":70,"title":71,"section":11},"\u002Fpkg\u002Fnavigation-menu","Navigation Menu",{"path":73,"title":74,"section":11},"\u002Fpkg\u002Fnumber-field","Number Field",{"path":76,"title":77,"section":11},"\u002Fpkg\u002Fotp-field","OTP Field",{"path":79,"title":80,"section":11},"\u002Fpkg\u002Fplot","Plot",{"path":82,"title":83,"section":11},"\u002Fpkg\u002Fpopover","Popover",{"path":85,"title":86,"section":11},"\u002Fpkg\u002Fpreview-card","Preview Card",{"path":88,"title":89,"section":11},"\u002Fpkg\u002Fprogress","Progress",{"path":91,"title":92,"section":11},"\u002Fpkg\u002Fradio-group","Radio Group",{"path":94,"title":95,"section":11},"\u002Fpkg\u002Fscroll-area","Scroll Area",{"path":97,"title":98,"section":11},"\u002Fpkg\u002Fselect","Select",{"path":100,"title":101,"section":11},"\u002Fpkg\u002Fseparator","Separator",{"path":103,"title":104,"section":11},"\u002Fpkg\u002Fsidebar","Sidebar",{"path":106,"title":107,"section":11},"\u002Fpkg\u002Fslider","Slider",{"path":109,"title":110,"section":11},"\u002Fpkg\u002Fswitch","Switch",{"path":112,"title":113,"section":11},"\u002Fpkg\u002Ftabs","Tabs",{"path":115,"title":116,"section":11},"\u002Fpkg\u002Ftoast","Toast",{"path":118,"title":119,"section":120},"\u002Fpkg\u002Fapi","Api","Runtime",{"path":122,"title":123,"section":120},"\u002Fpkg\u002Fauth","Auth",{"path":125,"title":126,"section":120},"\u002Fpkg\u002Fblob","Blob",{"path":128,"title":129,"section":120},"\u002Fpkg\u002Fchannels","Channels",{"path":131,"title":132,"section":120},"\u002Fpkg\u002Fdb","Db",{"id":134,"title":119,"body":135,"description":697,"extension":698,"meta":699,"navigation":271,"path":118,"section":120,"seo":700,"stem":701,"__hash__":702},"pkg\u002Fpkg\u002Fapi.md",{"type":136,"value":137,"toc":693},"minimark",[138,184,202,207,363,396,440,443,482,536,540,566,671,689],[139,140,141,145,146,149,150,153,154,153,157,153,160,163,164,167,168,171,172,175,176,179,180,183],"p",{},[142,143,144],"code",{},"lib\u002Fapi"," (",[142,147,148],{},"kalt-lib-api",") creates the HTTP methods and offers them to a service. ",[142,151,152],{},"get",", ",[142,155,156],{},"post",[142,158,159],{},"put",[142,161,162],{},"patch",", and ",[142,165,166],{},"delete"," build a route. ",[142,169,170],{},"serve"," and ",[142,173,174],{},"serve_after"," listen, and they register that list on the gateway. ",[142,177,178],{},"app\u002Fweb\u002Fapp\u002Flib\u002Fok-fetch.ts"," is the browser client. Both speak ",[142,181,182],{},"ApiResult\u003CT>"," from the contracts crate.",[139,185,186,187,190,191,194,195,198,199,201],{},"HTTP handlers never write Postgres. They publish onto that service's public channel with ",[142,188,189],{},"ch.pub","; the same binary's worker applies what ",[142,192,193],{},"ch.sub"," reads. The channel's model, owner, and subscribers are in ",[196,197,129],"a",{"href":128},". The publish check is part of ",[142,200,189],{},". The channels process stores the entry.",[203,204,206],"h2",{"id":205},"service","Service",[208,209,214],"pre",{"className":210,"code":211,"language":212,"meta":213,"style":213},"language-rust shiki shiki-themes github-light github-dark","pub fn router() -> kalt_lib_api::Api {\n    kalt_lib_api::Api::new()\n        .route(\n            \"\u002Fapi\u002Fpublications\",\n            kalt_lib_api::get(list_or_get).post(create),\n        )\n        .route(\"\u002Fapi\u002Fpublications\u002F{id}\u002Ftitle\", kalt_lib_api::put(put_title))\n}\n\nkalt_lib_api::serve_after(\n    \"publications\",\n    kalt_lib_api::Model::Domain,\n    \"0.0.0.0:8081\",\n    http::router(),\n    kalt_db::migrations!(),\n    |state| {\n        let worker = state.clone();\n        state.ch.follow(\"publications-1\", move |event| {\n            let state = worker.clone();\n            async move { apply::handle(state, event).await }\n        });\n    },\n)\n.await\n","rust","",[142,215,216,224,230,236,242,248,254,260,266,273,279,285,291,297,303,309,315,321,327,333,339,345,351,357],{"__ignoreMap":213},[217,218,221],"span",{"class":219,"line":220},"line",1,[217,222,223],{},"pub fn router() -> kalt_lib_api::Api {\n",[217,225,227],{"class":219,"line":226},2,[217,228,229],{},"    kalt_lib_api::Api::new()\n",[217,231,233],{"class":219,"line":232},3,[217,234,235],{},"        .route(\n",[217,237,239],{"class":219,"line":238},4,[217,240,241],{},"            \"\u002Fapi\u002Fpublications\",\n",[217,243,245],{"class":219,"line":244},5,[217,246,247],{},"            kalt_lib_api::get(list_or_get).post(create),\n",[217,249,251],{"class":219,"line":250},6,[217,252,253],{},"        )\n",[217,255,257],{"class":219,"line":256},7,[217,258,259],{},"        .route(\"\u002Fapi\u002Fpublications\u002F{id}\u002Ftitle\", kalt_lib_api::put(put_title))\n",[217,261,263],{"class":219,"line":262},8,[217,264,265],{},"}\n",[217,267,269],{"class":219,"line":268},9,[217,270,272],{"emptyLinePlaceholder":271},true,"\n",[217,274,276],{"class":219,"line":275},10,[217,277,278],{},"kalt_lib_api::serve_after(\n",[217,280,282],{"class":219,"line":281},11,[217,283,284],{},"    \"publications\",\n",[217,286,288],{"class":219,"line":287},12,[217,289,290],{},"    kalt_lib_api::Model::Domain,\n",[217,292,294],{"class":219,"line":293},13,[217,295,296],{},"    \"0.0.0.0:8081\",\n",[217,298,300],{"class":219,"line":299},14,[217,301,302],{},"    http::router(),\n",[217,304,306],{"class":219,"line":305},15,[217,307,308],{},"    kalt_db::migrations!(),\n",[217,310,312],{"class":219,"line":311},16,[217,313,314],{},"    |state| {\n",[217,316,318],{"class":219,"line":317},17,[217,319,320],{},"        let worker = state.clone();\n",[217,322,324],{"class":219,"line":323},18,[217,325,326],{},"        state.ch.follow(\"publications-1\", move |event| {\n",[217,328,330],{"class":219,"line":329},19,[217,331,332],{},"            let state = worker.clone();\n",[217,334,336],{"class":219,"line":335},20,[217,337,338],{},"            async move { apply::handle(state, event).await }\n",[217,340,342],{"class":219,"line":341},21,[217,343,344],{},"        });\n",[217,346,348],{"class":219,"line":347},22,[217,349,350],{},"    },\n",[217,352,354],{"class":219,"line":353},23,[217,355,356],{},")\n",[217,358,360],{"class":219,"line":359},24,[217,361,362],{},".await\n",[139,364,365,367,368,371,372,375,376,379,380,383,384,387,388,391,392,395],{},[142,366,170],{}," writes the method list to Redis at ",[142,369,370],{},"kalt.gw.methods:{service}"," and adds the service to ",[142,373,374],{},"kalt.gw.services",". The gateway reads that list. A request matches a registered verb and path before it uses the ",[142,377,378],{},"ok.yaml"," prefix table. ",[142,381,382],{},"public()"," on the methods just added leaves them open. ",[142,385,386],{},"open()"," does that for every method added so far, which is how auth and geo stay public. ",[142,389,390],{},"lead_deny()"," rejects a lead on the methods just added. ",[142,393,394],{},"lead_deny_all()"," does that for every method added so far.",[139,397,398,401,402,405,406,409,410,163,413,416,417,420,421,424,425,428,429,432,433,435,436,439],{},[142,399,400],{},"AppState::connect"," loads ",[142,403,404],{},"{SERVICE}_DATABASE_URL"," (or ",[142,407,408],{},"DATABASE_URL","), ",[142,411,412],{},"REDIS_URL",[142,414,415],{},"AUTH_JWT_SECRET",", runs the migrations the service passed, and binds ",[142,418,419],{},"Channel::public(service, model)",": the public stream ",[142,422,423],{},"kalt.{service}"," and the owner group ",[142,426,427],{},"kalt-{service}",". ",[142,430,431],{},"Ch::follow"," in ",[196,434,129],{"href":128}," is the subscribe loop, started from the service ",[142,437,438],{},"main",".",[139,441,442],{},"Extractors:",[444,445,446,456,462,471],"ul",{},[447,448,449,452,453,439],"li",{},[142,450,451],{},"OptionalUser"," — Bearer present and valid, or ",[142,454,455],{},"None",[447,457,458,461],{},[142,459,460],{},"SessionUser"," — signed in.",[447,463,464,467,468,439],{},[142,465,466],{},"EditorUser"," — ",[142,469,470],{},"role == \"editor\"",[447,472,473,467,476,171,479,439],{},[142,474,475],{},"MsgCtx",[142,477,478],{},"X-Msg-Source",[142,480,481],{},"X-Trace-Id",[139,483,484,487,488,490,491,494,495,498,499,502,503,506,507,510,511,490,514,153,517,153,520,153,523,153,526,153,529,532,533,439],{},[142,485,486],{},"emit_domain"," calls ",[142,489,189],{}," with ",[142,492,493],{},"type=domain"," on that public channel. A source channel, such as int-github, publishes ",[142,496,497],{},"type=source"," through the same envelope. The channels process stores both. ",[142,500,501],{},"wait_exists"," polls until the worker has written the row. ",[142,504,505],{},"ok(data)"," wraps ",[142,508,509],{},"{ data, success: true, error: null }",". Failures are ",[142,512,513],{},"ApiFail",[142,515,516],{},"unauthorized",[142,518,519],{},"not_editor",[142,521,522],{},"invalid",[142,524,525],{},"not_found",[142,527,528],{},"conflict",[142,530,531],{},"rate_limited",", or ",[142,534,535],{},"server_error",[203,537,539],{"id":538},"frontend","Frontend",[139,541,542,545,546,548,549,428,552,554,555,558,559,171,562,565],{},[142,543,544],{},"bindFetch(import.meta.url)"," stamps the calling file as ",[142,547,478],{}," and attaches the session ",[142,550,551],{},"traceId",[196,553,123],{"href":122}," adds ",[142,556,557],{},"Authorization: Bearer"," except on public ",[142,560,561],{},"\u002Fapi\u002Fauth",[142,563,564],{},"\u002Fapi\u002Fgeo"," paths.",[208,567,571],{"className":568,"code":569,"language":570,"meta":213,"style":213},"language-ts shiki shiki-themes github-light github-dark","import { bindFetch } from '~\u002Flib\u002Fok-fetch'\n\nconst okFetch = bindFetch(import.meta.url)\n\nconst result = await okFetch\u003CPublication>('\u002Fapi\u002Fpublication\u002Fledger\u002FAbC12~_x')\nif (!result.success) return\n","ts",[142,572,573,590,594,623,627,655],{"__ignoreMap":213},[217,574,575,579,583,586],{"class":219,"line":220},[217,576,578],{"class":577},"szBVR","import",[217,580,582],{"class":581},"sVt8B"," { bindFetch } ",[217,584,585],{"class":577},"from",[217,587,589],{"class":588},"sZZnC"," '~\u002Flib\u002Fok-fetch'\n",[217,591,592],{"class":219,"line":226},[217,593,272],{"emptyLinePlaceholder":271},[217,595,596,599,603,606,610,613,615,617,620],{"class":219,"line":232},[217,597,598],{"class":577},"const",[217,600,602],{"class":601},"sj4cs"," okFetch",[217,604,605],{"class":577}," =",[217,607,609],{"class":608},"sScJk"," bindFetch",[217,611,612],{"class":581},"(",[217,614,578],{"class":577},[217,616,439],{"class":581},[217,618,619],{"class":601},"meta",[217,621,622],{"class":581},".url)\n",[217,624,625],{"class":219,"line":238},[217,626,272],{"emptyLinePlaceholder":271},[217,628,629,631,634,636,639,641,644,647,650,653],{"class":219,"line":244},[217,630,598],{"class":577},[217,632,633],{"class":601}," result",[217,635,605],{"class":577},[217,637,638],{"class":577}," await",[217,640,602],{"class":608},[217,642,643],{"class":581},"\u003C",[217,645,646],{"class":608},"Publication",[217,648,649],{"class":581},">(",[217,651,652],{"class":588},"'\u002Fapi\u002Fpublication\u002Fledger\u002FAbC12~_x'",[217,654,356],{"class":581},[217,656,657,660,662,665,668],{"class":219,"line":250},[217,658,659],{"class":577},"if",[217,661,145],{"class":581},[217,663,664],{"class":577},"!",[217,666,667],{"class":581},"result.success) ",[217,669,670],{"class":577},"return\n",[139,672,673,676,677,680,681,684,685,688],{},[142,674,675],{},"fieldWrite(value)"," is ",[142,678,679],{},"{ tz, value }"," for ledger PATCHes. In production, ",[142,682,683],{},"okFetch"," sends ",[142,686,687],{},"\u002Fapi"," to the gateway.",[690,691,692],"style",{},"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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":213,"searchDepth":226,"depth":226,"links":694},[695,696],{"id":205,"depth":226,"text":206},{"id":538,"depth":226,"text":539},"HTTP methods a service offers, registered on the gateway","md",{},{"title":119,"description":697},"pkg\u002Fapi","dj7UaaImG9eqpD7NB3HrqgZ-4n5kqCKa868kYlIgr3g",1791043754620]