[{"data":1,"prerenderedAt":2832},["ShallowReactive",2],{"doc:\u002Fdocs\u002Fcapabilities\u002Fnotebooks":3,"docs-nav":2623},{"id":4,"title":5,"body":6,"description":2615,"extension":2616,"meta":2617,"navigation":417,"path":2619,"seo":2620,"stem":2621,"__hash__":2622},"docs\u002Fdocs\u002Fcapabilities\u002Fnotebooks.md","marimo notebooks as apps",{"type":7,"value":8,"toc":2596},"minimark",[9,13,30,37,48,53,172,176,181,204,210,221,224,255,261,269,283,296,299,308,314,319,326,330,337,354,361,368,379,383,390,489,500,503,517,535,539,552,656,723,745,749,752,755,777,784,788,795,824,827,830,840,908,911,915,932,952,956,959,971,974,985,1003,1014,1036,1069,1122,1135,1141,1145,1181,1222,2242,2245,2248,2303,2312,2323,2327,2409,2412,2419,2423,2556,2560,2592],[10,11,5],"h1",{"id":12},"marimo-notebooks-as-apps",[14,15,16,17,24,25,29],"p",{},"A notebook is a ",[18,19,23],"a",{"href":20,"rel":21},"https:\u002F\u002Fmarimo.io",[22],"nofollow","marimo"," notebook kept in a folder, like a flow or a\nsaved script: written in marimo's own reactive editor, saved with its versions, found in\nExplore and in the search, and shared through the folder's permissions. Whoever can see\nit opens it as a ",[26,27,28],"strong",{},"web app",", with its sliders, dropdowns, tables and charts, without\nseeing or touching the code. The notebook reads Tabularia's datasources when its code\nasks for them, with the rights of its author, and its pod runs only while somebody uses\nit.",[14,31,32],{},[33,34],"img",{"alt":35,"src":36},"The «Margin by category» app open in Tabularia: the threshold slider moves from 30% to 25% and then to 35%, the dashed line on the chart follows it and the count of category-months under it changes; then «Last months» goes from 12 to 24 and the chart is drawn again over two years","\u002Fmedia\u002Fguide\u002Fnotebooks\u002Fnotebook-app-sliders.gif",[14,38,39],{},[40,41,42,43,47],"em",{},"The app of the ",[18,44,46],{"href":45},"#example-margin-by-category","example below",", as whoever can see the\nnotebook opens it: each control recomputes only the cells that depend on it, and the code\nstays out of sight.",[49,50,52],"h2",{"id":51},"at-a-glance","At a glance",[54,55,56,67],"table",{},[57,58,59],"thead",{},[60,61,62,65],"tr",{},[63,64],"th",{},[63,66],{},[68,69,70,94,107,115,123,138,146,154],"tbody",{},[60,71,72,76],{},[73,74,75],"td",{},"Where",[73,77,78,79,82,83,86,87,90,91],{},"Explore → ",[26,80,81],{},"New"," → ",[26,84,85],{},"Notebook (marimo)",", in a folder; the notebook has its own page, with ",[26,88,89],{},"Open app"," and ",[26,92,93],{},"Open editor",[60,95,96,99],{},[73,97,98],{},"What it is",[73,100,101,102,106],{},"a marimo ",[103,104,105],"code",{},".py"," file, run in one of the Python scripting environments, whose image carries marimo",[60,108,109,112],{},[73,110,111],{},"The app",[73,113,114],{},"opened by whoever can see the notebook, on a page of its own; it runs the version marked current",[60,116,117,120],{},[73,118,119],{},"The editor",[73,121,122],{},"marimo's editor inside a Tabularia page, for whoever may edit the folder; one person at a time",[60,124,125,128],{},[73,126,127],{},"Data",[73,129,130,133,134,137],{},[103,131,132],{},"tabularia.read(\"Folder\u002FName\")"," returns a polars DataFrame, read when the code asks, nothing copied; ",[103,135,136],{},"tabularia.report(…)"," a report's PDF or Excel",[60,139,140,143],{},[73,141,142],{},"Whose rights",[73,144,145],{},"the app reads with the rights of whoever saved the version it runs; the editor with the editor's",[60,147,148,151],{},[73,149,150],{},"When it runs",[73,152,153],{},"its pod starts when someone opens it (a few seconds) and stops after 15 minutes with nobody on it",[60,155,156,159],{},[73,157,158],{},"Requires",[73,160,161,162,166,167,171],{},"Kubernetes, with the scripting environments (",[18,163,165],{"href":164},"\u002Fdocs\u002Fscripting","Python and R","), and a host name of their own for the apps and editors (",[18,168,170],{"href":169},"#an-address-of-their-own","An address of their own",")",[49,173,175],{"id":174},"how-it-works","How it works",[177,178,180],"h3",{"id":179},"the-object-and-its-versions","The object and its versions",[14,182,183,82,185,187,188,191,192,195,196,199,200,203],{},[26,184,81],{},[26,186,85],{}," asks for a name, a Python environment you may use and a\ndescription, and writes a starting notebook. Every save that changes the code is a new\n",[26,189,190],{},"version","; the newest one is the one the app runs. ",[26,193,194],{},"Versions"," on the notebook's page\nlists them with who saved each one; ",[26,197,198],{},"View"," shows an older one and ",[26,201,202],{},"Restore"," makes it\nthe newest again, as a new version.",[14,205,206],{},[33,207],{"alt":208,"src":209},"The page of the notebook «Margin by category» in the folder Sales analysis: Open app and Open editor at the top, a box saying the app reads Tabularia's data with the rights of Administrator, who saved this version, the Python environment, the description, the visitors at once, the source, and the versions v2 (current) and v1","\u002Fmedia\u002Fguide\u002Fnotebooks\u002Fnotebook-page.png",[14,211,212],{},[40,213,214,215,217,218,220],{},"The notebook's page: ",[26,216,89],{},", ",[26,219,93],{},", whose rights the app reads with, and\nthe versions on the right.",[14,222,223],{},"The code can be changed in two places:",[225,226,227,245],"ul",{},[228,229,230,233,234,236,237,240,241,244],"li",{},[26,231,232],{},"marimo's editor"," (",[26,235,93],{},"): the notebook runs live as you write it. marimo\nsaves the file in its pod; ",[26,238,239],{},"Save version",", in the bar above the editor, makes a\nTabularia version of what marimo saved last, with an optional note. ",[26,242,243],{},"Close editor","\nstops the editor.",[228,246,247,250,251,254],{},[26,248,249],{},"The notebook's page",", as text, then ",[26,252,253],{},"Save",".",[14,256,257],{},[33,258],{"alt":259,"src":260},"marimo's editor inside a Tabularia page: the bar above it says «You are editing: this editor reads Tabularia's data with your own rights» and holds Save version, Open app and Close editor; below, the cells of the example: the read of the datasource, the cell that draws the Category dropdown and the two sliders with its output, the filter, and the chart","\u002Fmedia\u002Fguide\u002Fnotebooks\u002Fnotebook-editor.png",[14,262,263],{},[40,264,265,266,268],{},"marimo's editor in its frame: each cell's code with its output under it. ",[26,267,239],{},"\nturns what marimo saved into the version the app runs.",[14,270,271,272,274,275,279,280,282],{},"While marimo's editor is open on a notebook, the code changes only there. The page shows\nthe source read-only, and Tabularia refuses a change of the source or a ",[26,273,202],{}," from\nanywhere else, saying who has the editor open: «Sales app» is open in marimo's editor by\n",[18,276,278],{"href":277},"mailto:ana@example.com","ana@example.com",": save or close it there first. ",[26,281,239],{}," in the editor goes on as\nusual.",[14,284,285,286,289,290,90,293,254],{},"What the editor saved and is not a version yet is kept as the notebook's ",[26,287,288],{},"draft",": when\nthe editor is closed, when its pod stops for inactivity, and at every round of the idle\ncheck. The next time the editor opens it starts from the draft. The notebook's page says\nthat there is one, with ",[26,291,292],{},"Load it",[26,294,295],{},"Discard",[177,297,111],{"id":298},"the-app",[14,300,301,303,304,307],{},[26,302,89],{}," opens the app in a new tab, on a page of its own. Behind it is ",[103,305,306],{},"marimo run",":\nthe viewers see the outputs and the controls, never the code. Everyone who opens the app\nshares the same pod; marimo keeps each open page's controls separate.",[14,309,310],{},[33,311],{"alt":312,"src":313},"The app on a page of its own: the title «Margin by category», a Category dropdown on All, the sliders Last months at 12 and Flag margins under (%) at 30, a line chart of the margin of five product categories over twelve months with a dashed line at 30%, and the start of the table of the 37 category-months under it","\u002Fmedia\u002Fguide\u002Fnotebooks\u002Fnotebook-app.png",[14,315,316],{},[40,317,318],{},"The app as its viewers see it: outputs and controls, no code. The bar above names the\nnotebook and its folder.",[14,320,321,322,325],{},"When a new version becomes current, the pod that runs the old one is replaced the next\ntime someone opens the app. ",[26,323,324],{},"Stop",", on the notebook's page, stops the app for\neveryone; the next visit starts it again.",[177,327,329],{"id":328},"with-whose-rights","With whose rights",[14,331,332,333,336],{},"The app reads data with the rights of ",[26,334,335],{},"the person who saved the version it runs",": the\nauthor of the current version, shown on the notebook's page («The app reads Tabularia's\ndata with the rights of …, who saved this version»). Its reads never use anyone else's\nrights:",[225,338,339,345,348,351],{},[228,340,341,344],{},[26,342,343],{},"Saving a version makes you its author."," Whoever saves a version, from the editor or\nfrom the page, or restores an older one, becomes the person the app reads as, from\nthat moment on. Restoring a version first written by someone else does not bring back\ntheir rights: the restored version is yours.",[228,346,347],{},"An app is a report its author publishes. Everyone who can open it sees what the\nauthor's code shows, even data in folders they could not read themselves. Before you\nsave a version, look at what it reads and at who can open the notebook.",[228,349,350],{},"If you can read less than the previous author, the reads you are not allowed fail with\na clear message; if you can read more, the app now shows what your rights allow.",[228,352,353],{},"The author must still exist, be active and be able to open the notebook (its folder and\nits environment). Otherwise the app does not start, and says that someone who may edit\nthe notebook must save or restore a version.",[14,355,356,357,360],{},"The editor reads with ",[26,358,359],{},"the editor's own rights",": what you see while you write is what\nyou may read.",[14,362,363,364,367],{},"What the code shows runs in the browser of whoever opens the app, HTML and JavaScript\nincluded; on the notebooks' ",[18,365,366],{"href":169},"address of their own"," it cannot\nuse their Tabularia session.",[14,369,370,371,374,375,378],{},"Rights are checked again at every start of a pod and at ",[26,372,373],{},"every read",": a permission\ntaken away stops the next ",[103,376,377],{},"tabularia.read",", without restarting anything.",[177,380,382],{"id":381},"reading-the-data","Reading the data",[14,384,385,386,389],{},"The ",[103,387,388],{},"tabularia"," module is in every notebook's pod, without rebuilding any image:",[391,392,397],"pre",{"className":393,"code":394,"language":395,"meta":396,"style":396},"language-python shiki shiki-themes github-dark-dimmed github-light","import tabularia\n\ntabularia.datasources()   # what this notebook may read: id, name, folder, path, rows, columns\ndf = tabularia.read(\"Sample\u002FPrivate tables\u002FMargin by Category and Month\")   # a polars DataFrame\ndf = tabularia.read(42, columns=[\"categoria\", \"margine\"])                   # by id, some columns\n","python","",[103,398,399,412,419,429,451],{"__ignoreMap":396},[400,401,404,408],"span",{"class":402,"line":403},"line",1,[400,405,407],{"class":406},"s6PUj","import",[400,409,411],{"class":410},"ssh_m"," tabularia\n",[400,413,415],{"class":402,"line":414},2,[400,416,418],{"emptyLinePlaceholder":417},true,"\n",[400,420,422,425],{"class":402,"line":421},3,[400,423,424],{"class":410},"tabularia.datasources()   ",[400,426,428],{"class":427},"sgHix","# what this notebook may read: id, name, folder, path, rows, columns\n",[400,430,432,435,438,441,445,448],{"class":402,"line":431},4,[400,433,434],{"class":410},"df ",[400,436,437],{"class":406},"=",[400,439,440],{"class":410}," tabularia.read(",[400,442,444],{"class":443},"sXfbr","\"Sample\u002FPrivate tables\u002FMargin by Category and Month\"",[400,446,447],{"class":410},")   ",[400,449,450],{"class":427},"# a polars DataFrame\n",[400,452,454,456,458,460,464,466,470,472,475,478,480,483,486],{"class":402,"line":453},5,[400,455,434],{"class":410},[400,457,437],{"class":406},[400,459,440],{"class":410},[400,461,463],{"class":462},"swcJU","42",[400,465,217],{"class":410},[400,467,469],{"class":468},"sNjOc","columns",[400,471,437],{"class":406},[400,473,474],{"class":410},"[",[400,476,477],{"class":443},"\"categoria\"",[400,479,217],{"class":410},[400,481,482],{"class":443},"\"margine\"",[400,484,485],{"class":410},"])                   ",[400,487,488],{"class":427},"# by id, some columns\n",[14,490,491,492,495,496,499],{},"A datasource is named by its id, by its path (folders and name, separated by ",[103,493,494],{},"\u002F","), or by\nits name alone when only one datasource you may read has it. Errors arrive as\n",[103,497,498],{},"tabularia.TabulariaError",", in words: not found, not allowed, the session ended.",[14,501,502],{},"Behind it, the pod asks the gateway for the datasource with a token of its own. The\ngateway checks the reader's rights and answers with a link to the datasource's parquet\nfile, valid for ten minutes, which the pod downloads from the object storage. Nothing is\ncopied, and the data is as fresh as the datasource: a refresh or a flow that publishes it\nagain is visible at the next read. Every read is in the audit log («Datasource read by a\nnotebook»).",[14,504,505,506,509,510,233,513,516],{},"The token lives only in the pod. It works only on the data endpoint, only from that pod's\naddress (a call carrying a forwarding header such as ",[103,507,508],{},"X-Forwarded-For"," is refused), and\nonly while the pod lives: a token the code prints in the app is useless anywhere else.\nBehind a service mesh (Istio, Linkerd) the gateway sees the sidecar's address instead of\nthe pod's: there, set ",[103,511,512],{},"scripts.notebooks.podIpCheck: false",[103,514,515],{},"NOTEBOOKS__POD_IP_CHECK",").",[14,518,519,522,523,525,526,530,531,534],{},[26,520,521],{},"Lineage."," The Lineage page draws a notebook as a reader of every datasource it has\nread with ",[103,524,377],{},"; with ",[18,527,529],{"href":528},"\u002Fdocs\u002Fopenlineage","OpenLineage"," on, each session of its\npod (app or editor) is a run of the job ",[103,532,533],{},"\u003Cfolder>\u002F\u003Cname> (notebook)",": START when the pod\nstarts, RUNNING at the first read of each datasource, COMPLETE with everything it read\nwhen it stops.",[177,536,538],{"id":537},"reports","Reports",[14,540,541,542,546,547,551],{},"A ",[18,543,545],{"href":544},"\u002Fdocs\u002Fcapabilities\u002Freports","pixel-perfect report"," comes into the notebook as a file, rendered with the same rights\nas the reads, exactly as the ",[18,548,550],{"href":549},"\u002Fdocs\u002Fapi#download-a-report","API"," renders it:",[391,553,555],{"className":393,"code":554,"language":395,"meta":396,"style":396},"import tabularia\n\ntabularia.reports()   # the reports this notebook may render: id, name, folder, path, parameters\npdf = tabularia.report(\"Sales\u002FMonthly sales\", region=\"North\", since=\"2026-09-01\")   # the PDF, as bytes\nxlsx = tabularia.report(12, format=\"xlsx\", region=[\"North\", \"South\"])              # by id, two regions, Excel\n",[103,556,557,563,567,575,613],{"__ignoreMap":396},[400,558,559,561],{"class":402,"line":403},[400,560,407],{"class":406},[400,562,411],{"class":410},[400,564,565],{"class":402,"line":414},[400,566,418],{"emptyLinePlaceholder":417},[400,568,569,572],{"class":402,"line":421},[400,570,571],{"class":410},"tabularia.reports()   ",[400,573,574],{"class":427},"# the reports this notebook may render: id, name, folder, path, parameters\n",[400,576,577,580,582,585,588,590,593,595,598,600,603,605,608,610],{"class":402,"line":431},[400,578,579],{"class":410},"pdf ",[400,581,437],{"class":406},[400,583,584],{"class":410}," tabularia.report(",[400,586,587],{"class":443},"\"Sales\u002FMonthly sales\"",[400,589,217],{"class":410},[400,591,592],{"class":468},"region",[400,594,437],{"class":406},[400,596,597],{"class":443},"\"North\"",[400,599,217],{"class":410},[400,601,602],{"class":468},"since",[400,604,437],{"class":406},[400,606,607],{"class":443},"\"2026-09-01\"",[400,609,447],{"class":410},[400,611,612],{"class":427},"# the PDF, as bytes\n",[400,614,615,618,620,622,625,627,630,632,635,637,639,641,643,645,647,650,653],{"class":402,"line":453},[400,616,617],{"class":410},"xlsx ",[400,619,437],{"class":406},[400,621,584],{"class":410},[400,623,624],{"class":462},"12",[400,626,217],{"class":410},[400,628,629],{"class":468},"format",[400,631,437],{"class":406},[400,633,634],{"class":443},"\"xlsx\"",[400,636,217],{"class":410},[400,638,592],{"class":468},[400,640,437],{"class":406},[400,642,474],{"class":410},[400,644,597],{"class":443},[400,646,217],{"class":410},[400,648,649],{"class":443},"\"South\"",[400,651,652],{"class":410},"])              ",[400,654,655],{"class":427},"# by id, two regions, Excel\n",[225,657,658,664,683,706],{},[228,659,660,663],{},[26,661,662],{},"The report"," is named as a datasource is: by its id, by its path, or by its name\nalone when only one report you may open has it.",[228,665,666,670,671,674,675,678,679,682],{},[26,667,668],{},[103,669,629],{},": ",[103,672,673],{},"pdf"," (the default), ",[103,676,677],{},"xlsx",", or ",[103,680,681],{},"html"," (the email's summary, as MHTML).",[228,684,685,688,689,692,693,696,697,701,702,705],{},[26,686,687],{},"The parameters"," are keyword arguments, by their names: a list for one that takes\nseveral values, a date as a ",[103,690,691],{},"datetime.date"," or as ",[103,694,695],{},"yyyy-mm-dd",", or a\n",[18,698,700],{"href":699},"\u002Fdocs\u002Fplaceholders","placeholder"," such as ",[103,703,704],{},"since=\"{start_of_month-1m}\"",". One left out\ntakes the report's default.",[228,707,708,711,712,715,716,719,720,254],{},[26,709,710],{},"The answer"," is the file's bytes: save them (",[103,713,714],{},"open(\"sales.pdf\", \"wb\").write(pdf)","), offer\nthem with ",[103,717,718],{},"mo.download(pdf, filename=\"sales.pdf\")",", or read the Excel with\n",[103,721,722],{},"polars.read_excel(io.BytesIO(xlsx))",[14,724,725,726,729,730,732,733,736,737,740,741,744],{},"The call waits while a worker renders the report, up to ",[103,727,728],{},"REPORTS__API_WAIT_SECONDS"," (120\nseconds by default). A value that does not fit, a report you cannot open, a dataset you\nmay not read or one over the row limit arrive as ",[103,731,498],{}," with the\ngateway's words (",[103,734,735],{},"Parameter «since»: '2026-02-30' is not a date…","). Every render is in the\naudit log (",[103,738,739],{},"report.render",", from ",[103,742,743],{},"notebook",", with the notebook's name).",[177,746,748],{"id":747},"starting-and-stopping","Starting and stopping",[14,750,751],{},"There is no pod for a notebook nobody is using. The first request to the app, or to the\neditor, creates one in the scripts namespace, with the image and the resources (CPU and\nmemory) of the notebook's environment. The page shows «Starting the app…» and opens the\nnotebook when it answers, usually in a few seconds. The first start on a node can take a\nminute or two while the node downloads the environment's image.",[14,753,754],{},"A pod stops:",[225,756,757,764,770],{},[228,758,759,760,763],{},"after ",[103,761,762],{},"NOTEBOOKS__IDLE_MINUTES"," (15 by default) with nobody using it; a page left open\nkeeps it alive;",[228,765,759,766,769],{},[103,767,768],{},"NOTEBOOKS__MAX_LIFETIME_HOURS"," (12 by default), used or not; the next visit\nstarts a new one;",[228,771,772,773,217,775,516],{},"when someone who may edit the notebook stops it (",[26,774,324],{},[26,776,243],{},[14,778,779,780,783],{},"At most ",[103,781,782],{},"NOTEBOOKS__MAX_LIVE"," pods (20 by default) run at once, apps and editors together;\npast it, opening another notebook says so. A pod that cannot start says why, in words: no\nnode free, an image that could not be downloaded, marimo missing from the image.",[177,785,787],{"id":786},"visitors-at-once-and-memory","Visitors at once, and memory",[14,789,790,791,794],{},"An app has one pod, but ",[26,792,793],{},"every person who opens it gets a kernel of their own"," inside\nthat pod: marimo runs the notebook once per visitor, so that one person's sliders do not\nmove another's. Each kernel holds its own copy of whatever the code read. An app that\nloads 1 GB of data, opened by ten people, holds ten copies; past the memory limit of the\nnotebook's environment Kubernetes stops the pod, and the app closes for everyone at once.",[14,796,797,798,801,802,805,806,809,810,813,814,817,818,217,821,516],{},"So an app takes a limited number of ",[26,799,800],{},"visitors at once",": 10 by default\n(",[103,803,804],{},"NOTEBOOKS__MAX_VISITORS_PER_APP","). Whoever may edit the notebook sets its own number on\nthe notebook's page (",[26,807,808],{},"Visitors at once","; empty = the default), up to the most the\ninstallation allows (",[103,811,812],{},"NOTEBOOKS__MAX_VISITORS_CEILING",", 50 by default). An administrator\nchanges both in ",[26,815,816],{},"Administration → Settings → Notebooks",", or in the Helm values\n(",[103,819,820],{},"scripts.notebooks.maxVisitorsPerApp",[103,822,823],{},"maxVisitorsCeiling",[14,825,826],{},"Past the limit, the next person sees «This app already has 10 people connected, the most\nit allows. It will open by itself as soon as someone leaves.»; the page checks again every\nten seconds and opens the app as soon as a place is free. A visitor is a browser tab with\nthe app open: closing the tab frees the place at once, and two tabs of the same person are\ntwo visitors. The count holds across all the gateway's replicas. The notebook's page shows\nhow many people are connected next to the running app. The editor has no limit: one person\nat a time edits a notebook.",[14,828,829],{},"How to choose the number: the environment's memory limit (Administration → Scripting\nenvironments), less some room for marimo itself, divided by what one visitor's data\ntakes. An environment with 4 GiB and an app whose cells hold about 300 MB of data takes\nten visitors comfortably; a lighter app, more.",[14,831,832,833,835,836,839],{},"The way to fit more people is to read less. ",[103,834,377],{}," downloads the whole\ndatasource unless told otherwise: read ",[26,837,838],{},"only the columns the app uses",", and filter or\naggregate early, so that what each kernel keeps is small.",[391,841,843],{"className":393,"code":842,"language":395,"meta":396,"style":396},"sales = tabularia.read(\"Sales\u002FOrders\", columns=[\"region\", \"month\", \"amount\"])\nby_region = sales.group_by(\"region\", \"month\").agg(pl.col(\"amount\").sum())  # keep this, not the rows\n",[103,844,845,881],{"__ignoreMap":396},[400,846,847,850,852,854,857,859,861,863,865,868,870,873,875,878],{"class":402,"line":403},[400,848,849],{"class":410},"sales ",[400,851,437],{"class":406},[400,853,440],{"class":410},[400,855,856],{"class":443},"\"Sales\u002FOrders\"",[400,858,217],{"class":410},[400,860,469],{"class":468},[400,862,437],{"class":406},[400,864,474],{"class":410},[400,866,867],{"class":443},"\"region\"",[400,869,217],{"class":410},[400,871,872],{"class":443},"\"month\"",[400,874,217],{"class":410},[400,876,877],{"class":443},"\"amount\"",[400,879,880],{"class":410},"])\n",[400,882,883,886,888,891,893,895,897,900,902,905],{"class":402,"line":414},[400,884,885],{"class":410},"by_region ",[400,887,437],{"class":406},[400,889,890],{"class":410}," sales.group_by(",[400,892,867],{"class":443},[400,894,217],{"class":410},[400,896,872],{"class":443},[400,898,899],{"class":410},").agg(pl.col(",[400,901,877],{"class":443},[400,903,904],{"class":410},").sum())  ",[400,906,907],{"class":427},"# keep this, not the rows\n",[14,909,910],{},"A cell that returns only the small result lets the large frame go when the cell has run.",[177,912,914],{"id":913},"the-environment","The environment",[14,916,917,918,920,921,90,924,927,928,931],{},"A notebook runs in a Python scripting environment, and every Python environment installs\n",[103,919,23],{}," with ",[103,922,923],{},"polars",[103,925,926],{},"pyarrow",". An image built before marimo joined those packages\ndoes not have it: an administrator builds it again (Administration → Scripting\nenvironments → ",[26,929,930],{},"Build again","). Other packages a notebook needs, a charting library for\nexample, go in the environment's package list like any other.",[14,933,934,935,939,940,217,943,217,946,217,949,516],{},"The environment's ",[18,936,938],{"href":937},"\u002Fdocs\u002Fscripting#variables","variables"," reach the notebook as they reach\na script: the plain ones in its environment, the secret ones and those handed over from\nTabularia through a Kubernetes Secret. They never replace what the notebook needs itself\n(",[103,941,942],{},"HOME",[103,944,945],{},"PYTHONPATH",[103,947,948],{},"TABULARIA_URL",[103,950,951],{},"TABULARIA_NOTEBOOK_TOKEN",[177,953,955],{"id":954},"the-network","The network",[14,957,958],{},"marimo answers without a password of its own, so a network policy lets only Tabularia's\ngateway and orchestrator into a notebook's pod: the gateway carries the browser's\nrequests and the WebSocket to it, for the people allowed; the orchestrator reads the\neditor's file to keep the draft.",[14,960,961,962,965,966,970],{},"Out of the pod, the notebooks follow the scripts' policy (",[103,963,964],{},"scripts.blockTabularia",", see\n",[18,967,969],{"href":968},"\u002Fdocs\u002Fscripting#network-policies","Network policies","), with one opening: the gateway's\npods, on their port, for the reads and the reports. The download goes straight to Tabularia's object\nstorage, at the address the engine uses: that address must be reachable from the scripts\nnamespace, as the scripts bucket is.",[177,972,170],{"id":973},"an-address-of-their-own",[14,975,976,977,980,981,984],{},"marimo shows whatever a notebook's code writes, HTML and JavaScript included (",[103,978,979],{},"mo.Html",",\n",[103,982,983],{},"mo.iframe",", a widget): that is how a notebook draws its charts and controls, and that\ncode runs in the browser of whoever opens the app or the editor. Served from Tabularia's\nown address, it would run with their Tabularia session: someone who may edit a notebook\ncould act as whoever opens it, an administrator included, and read or change whatever\nthey may.",[14,986,987,988,980,991,994,995,998,999,1002],{},"So the apps and the editors are served from ",[26,989,990],{},"an address of their own",[103,992,993],{},"NOTEBOOKS__ORIGIN",", for example ",[103,996,997],{},"https:\u002F\u002Fnotebooks.example.com"," next to\n",[103,1000,1001],{},"https:\u002F\u002Ftabularia.example.com",":",[225,1004,1005,1008,1011],{},[228,1006,1007],{},"There the gateway answers only for the notebooks' apps and editors: none of\nTabularia's API is on that address, and Tabularia's own address does not answer\nrequests sent from it. A notebook's code finds nothing of Tabularia to call.",[228,1009,1010],{},"Tabularia's page opens the notebook's frame with a ticket that works once, within a\nminute, for that person, that notebook and that mode (app or editor), with the same\nrights it takes to open them. The frame then holds with a cookie of its own, valid\nonly for that notebook and only while the person's Tabularia session lasts: signing\nout closes the frames too. Tabularia's session cookie and API tokens are never\naccepted there.",[228,1012,1013],{},"Only the Tabularia page that opened it may show the notebook in a frame.",[14,1015,1016,1019,1020,1023,1024,1027,1028,1031,1032,1035],{},[26,1017,1018],{},"Setting it up."," A host name that reaches the same Ingress as Tabularia and, with TLS,\na certificate for it. A ",[40,1021,1022],{},"sibling"," of the interface's host is best (",[103,1025,1026],{},"notebooks.example.com","\nwith ",[103,1029,1030],{},"tabularia.example.com","): the browser treats the frame's cookie as the site's own.\nAnother domain works too, because the cookie is ",[40,1033,1034],{},"partitioned",": a third party's cookie\nthat Chrome, Edge and Firefox keep even when they block the others, for this one site.\nWith Helm:",[391,1037,1041],{"className":1038,"code":1039,"language":1040,"meta":396,"style":396},"language-yaml shiki shiki-themes github-dark-dimmed github-light","scripts:\n  notebooks:\n    host: notebooks.example.com\n","yaml",[103,1042,1043,1052,1059],{"__ignoreMap":396},[400,1044,1045,1049],{"class":402,"line":403},[400,1046,1048],{"class":1047},"shb1k","scripts",[400,1050,1051],{"class":410},":\n",[400,1053,1054,1057],{"class":402,"line":414},[400,1055,1056],{"class":1047},"  notebooks",[400,1058,1051],{"class":410},[400,1060,1061,1064,1066],{"class":402,"line":421},[400,1062,1063],{"class":1047},"    host",[400,1065,670],{"class":410},[400,1067,1068],{"class":443},"notebooks.example.com\n",[14,1070,1071,1072,1075,1076,1079,1080,1082,1083,1086,1087,1089,1090,1093,1094,678,1097,1100,1101,1104,1105,90,1109,1113,1114,1117,1118,1121],{},"The chart adds the host to the Ingress, towards the gateway alone under ",[103,1073,1074],{},"\u002Fapi",", with a\ncertificate when the Ingress has TLS (",[103,1077,1078],{},"scripts.notebooks.tlsSecretName","; cert-manager\nmakes it from the Ingress's annotations), and tells the gateway (",[103,1081,993],{},") and\nthe frontend (",[103,1084,1085],{},"NUXT_PUBLIC_NOTEBOOKS_ORIGIN",", which lets Tabularia's pages frame it).\nWithout the chart, set both variables to the same address and send that host's ",[103,1088,1074],{}," to\nthe gateway with its ",[103,1091,1092],{},"Host"," header unchanged (the gateway tells the two addresses apart\nby it; ingress controllers keep it). The address is ",[103,1095,1096],{},"https",[103,1098,1099],{},"http"," only for ",[103,1102,1103],{},"localhost"," and its subdomains:\nthe guides for ",[18,1106,1108],{"href":1107},"\u002Fdocs\u002Fdeploy\u002Fkind","kind",[18,1110,1112],{"href":1111},"\u002Fdocs\u002Fdeploy\u002Fminikube","minikube"," use\n",[103,1115,1116],{},"http:\u002F\u002Fnotebooks.localhost:8080",", which a browser finds on its own computer without any\nDNS. Never list it in ",[103,1119,1120],{},"APP__CORS_ORIGINS",": the gateway refuses to start.",[14,1123,1124,1127,1128,233,1131,1134],{},[26,1125,1126],{},"Without it"," the apps and the editors do not open, and their pages say: «Notebooks\nneed an address of their own (NOTEBOOKS__ORIGIN): an administrator must set it».\n",[103,1129,1130],{},"NOTEBOOKS__ALLOW_SAME_ORIGIN=true",[103,1132,1133],{},"scripts.notebooks.allowSameOrigin",") serves them on\nTabularia's own address instead, as before: only to try notebooks alone on one machine,\nnever where other people sign in. The pages of the app and of the editor then show a\nwarning.",[14,1136,1137,1140],{},[26,1138,1139],{},"What remains."," The notebooks share their one address. A notebook's code can\ntherefore reach another notebook that the same person opened in the same browser within\nthe hour: read that app as they see it and, while their editor of it is running, run\ncode in it with their rights. It cannot start anyone's editor (only Tabularia's page\ndoes) and reaches nothing else of Tabularia. Give EDIT on folders with notebooks to\npeople you would trust with what their colleagues can read.",[49,1142,1144],{"id":1143},"example-margin-by-category","Example: margin by category",[14,1146,1147,1148,1151,1152,217,1155,233,1158,1161,1162,217,1165,217,1168,217,1171,217,1174,90,1177,1180],{},"The sample data ships a datasource «Margin by Category and Month», published by the\nsample flow of the same name in ",[26,1149,1150],{},"Sample \u002F Private tables",": one row per product\ncategory and month, with ",[103,1153,1154],{},"categoria",[103,1156,1157],{},"anno_mese",[103,1159,1160],{},"202510"," for October 2025),\n",[103,1163,1164],{},"ricavo",[103,1166,1167],{},"costo",[103,1169,1170],{},"margine",[103,1172,1173],{},"chili",[103,1175,1176],{},"ordini",[103,1178,1179],{},"margine_pct",". This notebook turns it\ninto an app where a sales manager picks a category and a period and sees the months that\nearned too little.",[1182,1183,1184,1195,1203,1209,1219],"ol",{},[228,1185,1186,1187,1190,1191,1194],{},"The chart uses ",[103,1188,1189],{},"altair",". In Administration → Scripting environments, add\n",[103,1192,1193],{},"altair==5.5.0"," to the packages of a Python environment and save: the environment\nbuilds a new version.",[228,1196,1197,1198,82,1200,1202],{},"In Explore, open a folder you may edit, ",[26,1199,81],{},[26,1201,85],{},", name it\n«Margin by category», choose that environment.",[228,1204,1205,1206,1208],{},"On the notebook's page, replace the starting source with the code below and ",[26,1207,253],{},":\nversion 2, with you as its author.",[228,1210,1211,1213,1214,1216,1217,254],{},[26,1212,89],{},". To change it, ",[26,1215,93],{},", work in marimo, then ",[26,1218,239],{},[228,1220,1221],{},"Give your colleagues VIEW on the folder, and the use of the environment: they open\nthe app, and it reads with your rights.",[391,1223,1225],{"className":393,"code":1224,"language":395,"meta":396,"style":396},"import marimo\n\napp = marimo.App(width=\"medium\")\n\n\n@app.cell\ndef _():\n    import altair as alt\n    import marimo as mo\n    import polars as pl\n\n    import tabularia\n    return alt, mo, pl, tabularia\n\n\n@app.cell\ndef _(mo):\n    mo.md(\"# Margin by category\")\n    return\n\n\n@app.cell\ndef _(pl, tabularia):\n    margin = tabularia.read(\"Sample\u002FPrivate tables\u002FMargin by Category and Month\").with_columns(\n        month=pl.col(\"anno_mese\").cast(pl.Int64).cast(pl.Utf8)\n    )\n    return (margin,)\n\n\n@app.cell\ndef _(margin, mo):\n    months = sorted(margin[\"month\"].unique().to_list())\n    categories = sorted(margin[\"categoria\"].unique().to_list())\n    category = mo.ui.dropdown(options=[\"All\", *categories], value=\"All\", label=\"Category\")\n    last = mo.ui.slider(start=3, stop=len(months), value=min(12, len(months)), label=\"Last months\",\n                        show_value=True)\n    floor = mo.ui.slider(start=0, stop=60, step=5, value=30, label=\"Flag margins under (%)\",\n                         show_value=True)\n    mo.hstack([category, last, floor], justify=\"start\", gap=2)\n    return category, floor, last, months\n\n\n@app.cell\ndef _(category, last, margin, months, pl):\n    shown = margin.filter(pl.col(\"month\").is_in(months[-last.value:]))\n    if category.value != \"All\":\n        shown = shown.filter(pl.col(\"categoria\") == category.value)\n    return (shown,)\n\n\n@app.cell\ndef _(alt, floor, shown):\n    lines = alt.Chart(shown).mark_line(point=True).encode(\n        x=alt.X(\"month:O\", title=\"Month\"),\n        y=alt.Y(\"margine_pct:Q\", title=\"Margin (%)\"),\n        color=alt.Color(\"categoria:N\", title=\"Category\"),\n        tooltip=[\"categoria\", \"month\", \"ricavo\", \"margine\", \"margine_pct\"],\n    )\n    threshold = alt.Chart(shown).mark_rule(strokeDash=[4, 4], color=\"gray\").encode(y=alt.datum(floor.value))\n    (lines + threshold).properties(height=320)\n    return\n\n\n@app.cell\ndef _(floor, mo, pl, shown):\n    under = (shown.filter(pl.col(\"margine_pct\") \u003C floor.value)\n             .select(\"month\", \"categoria\", pl.col(\"ricavo\", \"margine\").round(0), \"margine_pct\")\n             .sort(\"month\", \"categoria\"))\n    mo.vstack([mo.md(f\"**{under.height}** category-months under {floor.value}%\"), mo.ui.table(under)])\n    return\n\n\nif __name__ == \"__main__\":\n    app.run()\n",[103,1226,1227,1234,1238,1259,1263,1267,1274,1286,1301,1314,1327,1332,1339,1348,1353,1358,1363,1373,1384,1390,1395,1400,1405,1415,1430,1447,1453,1461,1466,1471,1476,1486,1505,1521,1569,1629,1642,1698,1710,1736,1744,1749,1754,1759,1769,1791,1808,1830,1838,1843,1848,1853,1863,1884,1911,1936,1960,1993,1998,2045,2067,2072,2077,2082,2087,2097,2118,2151,2166,2204,2209,2214,2219,2236],{"__ignoreMap":396},[400,1228,1229,1231],{"class":402,"line":403},[400,1230,407],{"class":406},[400,1232,1233],{"class":410}," marimo\n",[400,1235,1236],{"class":402,"line":414},[400,1237,418],{"emptyLinePlaceholder":417},[400,1239,1240,1243,1245,1248,1251,1253,1256],{"class":402,"line":421},[400,1241,1242],{"class":410},"app ",[400,1244,437],{"class":406},[400,1246,1247],{"class":410}," marimo.App(",[400,1249,1250],{"class":468},"width",[400,1252,437],{"class":406},[400,1254,1255],{"class":443},"\"medium\"",[400,1257,1258],{"class":410},")\n",[400,1260,1261],{"class":402,"line":431},[400,1262,418],{"emptyLinePlaceholder":417},[400,1264,1265],{"class":402,"line":453},[400,1266,418],{"emptyLinePlaceholder":417},[400,1268,1270],{"class":402,"line":1269},6,[400,1271,1273],{"class":1272},"saVmf","@app.cell\n",[400,1275,1277,1280,1283],{"class":402,"line":1276},7,[400,1278,1279],{"class":406},"def",[400,1281,1282],{"class":1272}," _",[400,1284,1285],{"class":410},"():\n",[400,1287,1289,1292,1295,1298],{"class":402,"line":1288},8,[400,1290,1291],{"class":406},"    import",[400,1293,1294],{"class":410}," altair ",[400,1296,1297],{"class":406},"as",[400,1299,1300],{"class":410}," alt\n",[400,1302,1304,1306,1309,1311],{"class":402,"line":1303},9,[400,1305,1291],{"class":406},[400,1307,1308],{"class":410}," marimo ",[400,1310,1297],{"class":406},[400,1312,1313],{"class":410}," mo\n",[400,1315,1317,1319,1322,1324],{"class":402,"line":1316},10,[400,1318,1291],{"class":406},[400,1320,1321],{"class":410}," polars ",[400,1323,1297],{"class":406},[400,1325,1326],{"class":410}," pl\n",[400,1328,1330],{"class":402,"line":1329},11,[400,1331,418],{"emptyLinePlaceholder":417},[400,1333,1335,1337],{"class":402,"line":1334},12,[400,1336,1291],{"class":406},[400,1338,411],{"class":410},[400,1340,1342,1345],{"class":402,"line":1341},13,[400,1343,1344],{"class":406},"    return",[400,1346,1347],{"class":410}," alt, mo, pl, tabularia\n",[400,1349,1351],{"class":402,"line":1350},14,[400,1352,418],{"emptyLinePlaceholder":417},[400,1354,1356],{"class":402,"line":1355},15,[400,1357,418],{"emptyLinePlaceholder":417},[400,1359,1361],{"class":402,"line":1360},16,[400,1362,1273],{"class":1272},[400,1364,1366,1368,1370],{"class":402,"line":1365},17,[400,1367,1279],{"class":406},[400,1369,1282],{"class":1272},[400,1371,1372],{"class":410},"(mo):\n",[400,1374,1376,1379,1382],{"class":402,"line":1375},18,[400,1377,1378],{"class":410},"    mo.md(",[400,1380,1381],{"class":443},"\"# Margin by category\"",[400,1383,1258],{"class":410},[400,1385,1387],{"class":402,"line":1386},19,[400,1388,1389],{"class":406},"    return\n",[400,1391,1393],{"class":402,"line":1392},20,[400,1394,418],{"emptyLinePlaceholder":417},[400,1396,1398],{"class":402,"line":1397},21,[400,1399,418],{"emptyLinePlaceholder":417},[400,1401,1403],{"class":402,"line":1402},22,[400,1404,1273],{"class":1272},[400,1406,1408,1410,1412],{"class":402,"line":1407},23,[400,1409,1279],{"class":406},[400,1411,1282],{"class":1272},[400,1413,1414],{"class":410},"(pl, tabularia):\n",[400,1416,1418,1421,1423,1425,1427],{"class":402,"line":1417},24,[400,1419,1420],{"class":410},"    margin ",[400,1422,437],{"class":406},[400,1424,440],{"class":410},[400,1426,444],{"class":443},[400,1428,1429],{"class":410},").with_columns(\n",[400,1431,1433,1436,1438,1441,1444],{"class":402,"line":1432},25,[400,1434,1435],{"class":468},"        month",[400,1437,437],{"class":406},[400,1439,1440],{"class":410},"pl.col(",[400,1442,1443],{"class":443},"\"anno_mese\"",[400,1445,1446],{"class":410},").cast(pl.Int64).cast(pl.Utf8)\n",[400,1448,1450],{"class":402,"line":1449},26,[400,1451,1452],{"class":410},"    )\n",[400,1454,1456,1458],{"class":402,"line":1455},27,[400,1457,1344],{"class":406},[400,1459,1460],{"class":410}," (margin,)\n",[400,1462,1464],{"class":402,"line":1463},28,[400,1465,418],{"emptyLinePlaceholder":417},[400,1467,1469],{"class":402,"line":1468},29,[400,1470,418],{"emptyLinePlaceholder":417},[400,1472,1474],{"class":402,"line":1473},30,[400,1475,1273],{"class":1272},[400,1477,1479,1481,1483],{"class":402,"line":1478},31,[400,1480,1279],{"class":406},[400,1482,1282],{"class":1272},[400,1484,1485],{"class":410},"(margin, mo):\n",[400,1487,1489,1492,1494,1497,1500,1502],{"class":402,"line":1488},32,[400,1490,1491],{"class":410},"    months ",[400,1493,437],{"class":406},[400,1495,1496],{"class":462}," sorted",[400,1498,1499],{"class":410},"(margin[",[400,1501,872],{"class":443},[400,1503,1504],{"class":410},"].unique().to_list())\n",[400,1506,1508,1511,1513,1515,1517,1519],{"class":402,"line":1507},33,[400,1509,1510],{"class":410},"    categories ",[400,1512,437],{"class":406},[400,1514,1496],{"class":462},[400,1516,1499],{"class":410},[400,1518,477],{"class":443},[400,1520,1504],{"class":410},[400,1522,1524,1527,1529,1532,1535,1537,1539,1542,1544,1547,1550,1553,1555,1557,1559,1562,1564,1567],{"class":402,"line":1523},34,[400,1525,1526],{"class":410},"    category ",[400,1528,437],{"class":406},[400,1530,1531],{"class":410}," mo.ui.dropdown(",[400,1533,1534],{"class":468},"options",[400,1536,437],{"class":406},[400,1538,474],{"class":410},[400,1540,1541],{"class":443},"\"All\"",[400,1543,217],{"class":410},[400,1545,1546],{"class":406},"*",[400,1548,1549],{"class":410},"categories], ",[400,1551,1552],{"class":468},"value",[400,1554,437],{"class":406},[400,1556,1541],{"class":443},[400,1558,217],{"class":410},[400,1560,1561],{"class":468},"label",[400,1563,437],{"class":406},[400,1565,1566],{"class":443},"\"Category\"",[400,1568,1258],{"class":410},[400,1570,1572,1575,1577,1580,1583,1585,1588,1590,1593,1595,1598,1601,1603,1605,1608,1611,1613,1615,1617,1620,1622,1624,1627],{"class":402,"line":1571},35,[400,1573,1574],{"class":410},"    last ",[400,1576,437],{"class":406},[400,1578,1579],{"class":410}," mo.ui.slider(",[400,1581,1582],{"class":468},"start",[400,1584,437],{"class":406},[400,1586,1587],{"class":462},"3",[400,1589,217],{"class":410},[400,1591,1592],{"class":468},"stop",[400,1594,437],{"class":406},[400,1596,1597],{"class":462},"len",[400,1599,1600],{"class":410},"(months), ",[400,1602,1552],{"class":468},[400,1604,437],{"class":406},[400,1606,1607],{"class":462},"min",[400,1609,1610],{"class":410},"(",[400,1612,624],{"class":462},[400,1614,217],{"class":410},[400,1616,1597],{"class":462},[400,1618,1619],{"class":410},"(months)), ",[400,1621,1561],{"class":468},[400,1623,437],{"class":406},[400,1625,1626],{"class":443},"\"Last months\"",[400,1628,980],{"class":410},[400,1630,1632,1635,1637,1640],{"class":402,"line":1631},36,[400,1633,1634],{"class":468},"                        show_value",[400,1636,437],{"class":406},[400,1638,1639],{"class":462},"True",[400,1641,1258],{"class":410},[400,1643,1645,1648,1650,1652,1654,1656,1659,1661,1663,1665,1668,1670,1673,1675,1678,1680,1682,1684,1687,1689,1691,1693,1696],{"class":402,"line":1644},37,[400,1646,1647],{"class":410},"    floor ",[400,1649,437],{"class":406},[400,1651,1579],{"class":410},[400,1653,1582],{"class":468},[400,1655,437],{"class":406},[400,1657,1658],{"class":462},"0",[400,1660,217],{"class":410},[400,1662,1592],{"class":468},[400,1664,437],{"class":406},[400,1666,1667],{"class":462},"60",[400,1669,217],{"class":410},[400,1671,1672],{"class":468},"step",[400,1674,437],{"class":406},[400,1676,1677],{"class":462},"5",[400,1679,217],{"class":410},[400,1681,1552],{"class":468},[400,1683,437],{"class":406},[400,1685,1686],{"class":462},"30",[400,1688,217],{"class":410},[400,1690,1561],{"class":468},[400,1692,437],{"class":406},[400,1694,1695],{"class":443},"\"Flag margins under (%)\"",[400,1697,980],{"class":410},[400,1699,1701,1704,1706,1708],{"class":402,"line":1700},38,[400,1702,1703],{"class":468},"                         show_value",[400,1705,437],{"class":406},[400,1707,1639],{"class":462},[400,1709,1258],{"class":410},[400,1711,1713,1716,1719,1721,1724,1726,1729,1731,1734],{"class":402,"line":1712},39,[400,1714,1715],{"class":410},"    mo.hstack([category, last, floor], ",[400,1717,1718],{"class":468},"justify",[400,1720,437],{"class":406},[400,1722,1723],{"class":443},"\"start\"",[400,1725,217],{"class":410},[400,1727,1728],{"class":468},"gap",[400,1730,437],{"class":406},[400,1732,1733],{"class":462},"2",[400,1735,1258],{"class":410},[400,1737,1739,1741],{"class":402,"line":1738},40,[400,1740,1344],{"class":406},[400,1742,1743],{"class":410}," category, floor, last, months\n",[400,1745,1747],{"class":402,"line":1746},41,[400,1748,418],{"emptyLinePlaceholder":417},[400,1750,1752],{"class":402,"line":1751},42,[400,1753,418],{"emptyLinePlaceholder":417},[400,1755,1757],{"class":402,"line":1756},43,[400,1758,1273],{"class":1272},[400,1760,1762,1764,1766],{"class":402,"line":1761},44,[400,1763,1279],{"class":406},[400,1765,1282],{"class":1272},[400,1767,1768],{"class":410},"(category, last, margin, months, pl):\n",[400,1770,1772,1775,1777,1780,1782,1785,1788],{"class":402,"line":1771},45,[400,1773,1774],{"class":410},"    shown ",[400,1776,437],{"class":406},[400,1778,1779],{"class":410}," margin.filter(pl.col(",[400,1781,872],{"class":443},[400,1783,1784],{"class":410},").is_in(months[",[400,1786,1787],{"class":406},"-",[400,1789,1790],{"class":410},"last.value:]))\n",[400,1792,1794,1797,1800,1803,1806],{"class":402,"line":1793},46,[400,1795,1796],{"class":406},"    if",[400,1798,1799],{"class":410}," category.value ",[400,1801,1802],{"class":406},"!=",[400,1804,1805],{"class":443}," \"All\"",[400,1807,1051],{"class":410},[400,1809,1811,1814,1816,1819,1821,1824,1827],{"class":402,"line":1810},47,[400,1812,1813],{"class":410},"        shown ",[400,1815,437],{"class":406},[400,1817,1818],{"class":410}," shown.filter(pl.col(",[400,1820,477],{"class":443},[400,1822,1823],{"class":410},") ",[400,1825,1826],{"class":406},"==",[400,1828,1829],{"class":410}," category.value)\n",[400,1831,1833,1835],{"class":402,"line":1832},48,[400,1834,1344],{"class":406},[400,1836,1837],{"class":410}," (shown,)\n",[400,1839,1841],{"class":402,"line":1840},49,[400,1842,418],{"emptyLinePlaceholder":417},[400,1844,1846],{"class":402,"line":1845},50,[400,1847,418],{"emptyLinePlaceholder":417},[400,1849,1851],{"class":402,"line":1850},51,[400,1852,1273],{"class":1272},[400,1854,1856,1858,1860],{"class":402,"line":1855},52,[400,1857,1279],{"class":406},[400,1859,1282],{"class":1272},[400,1861,1862],{"class":410},"(alt, floor, shown):\n",[400,1864,1866,1869,1871,1874,1877,1879,1881],{"class":402,"line":1865},53,[400,1867,1868],{"class":410},"    lines ",[400,1870,437],{"class":406},[400,1872,1873],{"class":410}," alt.Chart(shown).mark_line(",[400,1875,1876],{"class":468},"point",[400,1878,437],{"class":406},[400,1880,1639],{"class":462},[400,1882,1883],{"class":410},").encode(\n",[400,1885,1887,1890,1892,1895,1898,1900,1903,1905,1908],{"class":402,"line":1886},54,[400,1888,1889],{"class":468},"        x",[400,1891,437],{"class":406},[400,1893,1894],{"class":410},"alt.X(",[400,1896,1897],{"class":443},"\"month:O\"",[400,1899,217],{"class":410},[400,1901,1902],{"class":468},"title",[400,1904,437],{"class":406},[400,1906,1907],{"class":443},"\"Month\"",[400,1909,1910],{"class":410},"),\n",[400,1912,1914,1917,1919,1922,1925,1927,1929,1931,1934],{"class":402,"line":1913},55,[400,1915,1916],{"class":468},"        y",[400,1918,437],{"class":406},[400,1920,1921],{"class":410},"alt.Y(",[400,1923,1924],{"class":443},"\"margine_pct:Q\"",[400,1926,217],{"class":410},[400,1928,1902],{"class":468},[400,1930,437],{"class":406},[400,1932,1933],{"class":443},"\"Margin (%)\"",[400,1935,1910],{"class":410},[400,1937,1939,1942,1944,1947,1950,1952,1954,1956,1958],{"class":402,"line":1938},56,[400,1940,1941],{"class":468},"        color",[400,1943,437],{"class":406},[400,1945,1946],{"class":410},"alt.Color(",[400,1948,1949],{"class":443},"\"categoria:N\"",[400,1951,217],{"class":410},[400,1953,1902],{"class":468},[400,1955,437],{"class":406},[400,1957,1566],{"class":443},[400,1959,1910],{"class":410},[400,1961,1963,1966,1968,1970,1972,1974,1976,1978,1981,1983,1985,1987,1990],{"class":402,"line":1962},57,[400,1964,1965],{"class":468},"        tooltip",[400,1967,437],{"class":406},[400,1969,474],{"class":410},[400,1971,477],{"class":443},[400,1973,217],{"class":410},[400,1975,872],{"class":443},[400,1977,217],{"class":410},[400,1979,1980],{"class":443},"\"ricavo\"",[400,1982,217],{"class":410},[400,1984,482],{"class":443},[400,1986,217],{"class":410},[400,1988,1989],{"class":443},"\"margine_pct\"",[400,1991,1992],{"class":410},"],\n",[400,1994,1996],{"class":402,"line":1995},58,[400,1997,1452],{"class":410},[400,1999,2001,2004,2006,2009,2012,2014,2016,2019,2021,2023,2026,2029,2031,2034,2037,2040,2042],{"class":402,"line":2000},59,[400,2002,2003],{"class":410},"    threshold ",[400,2005,437],{"class":406},[400,2007,2008],{"class":410}," alt.Chart(shown).mark_rule(",[400,2010,2011],{"class":468},"strokeDash",[400,2013,437],{"class":406},[400,2015,474],{"class":410},[400,2017,2018],{"class":462},"4",[400,2020,217],{"class":410},[400,2022,2018],{"class":462},[400,2024,2025],{"class":410},"], ",[400,2027,2028],{"class":468},"color",[400,2030,437],{"class":406},[400,2032,2033],{"class":443},"\"gray\"",[400,2035,2036],{"class":410},").encode(",[400,2038,2039],{"class":468},"y",[400,2041,437],{"class":406},[400,2043,2044],{"class":410},"alt.datum(floor.value))\n",[400,2046,2048,2051,2054,2057,2060,2062,2065],{"class":402,"line":2047},60,[400,2049,2050],{"class":410},"    (lines ",[400,2052,2053],{"class":406},"+",[400,2055,2056],{"class":410}," threshold).properties(",[400,2058,2059],{"class":468},"height",[400,2061,437],{"class":406},[400,2063,2064],{"class":462},"320",[400,2066,1258],{"class":410},[400,2068,2070],{"class":402,"line":2069},61,[400,2071,1389],{"class":406},[400,2073,2075],{"class":402,"line":2074},62,[400,2076,418],{"emptyLinePlaceholder":417},[400,2078,2080],{"class":402,"line":2079},63,[400,2081,418],{"emptyLinePlaceholder":417},[400,2083,2085],{"class":402,"line":2084},64,[400,2086,1273],{"class":1272},[400,2088,2090,2092,2094],{"class":402,"line":2089},65,[400,2091,1279],{"class":406},[400,2093,1282],{"class":1272},[400,2095,2096],{"class":410},"(floor, mo, pl, shown):\n",[400,2098,2100,2103,2105,2108,2110,2112,2115],{"class":402,"line":2099},66,[400,2101,2102],{"class":410},"    under ",[400,2104,437],{"class":406},[400,2106,2107],{"class":410}," (shown.filter(pl.col(",[400,2109,1989],{"class":443},[400,2111,1823],{"class":410},[400,2113,2114],{"class":406},"\u003C",[400,2116,2117],{"class":410}," floor.value)\n",[400,2119,2121,2124,2126,2128,2130,2133,2135,2137,2139,2142,2144,2147,2149],{"class":402,"line":2120},67,[400,2122,2123],{"class":410},"             .select(",[400,2125,872],{"class":443},[400,2127,217],{"class":410},[400,2129,477],{"class":443},[400,2131,2132],{"class":410},", pl.col(",[400,2134,1980],{"class":443},[400,2136,217],{"class":410},[400,2138,482],{"class":443},[400,2140,2141],{"class":410},").round(",[400,2143,1658],{"class":462},[400,2145,2146],{"class":410},"), ",[400,2148,1989],{"class":443},[400,2150,1258],{"class":410},[400,2152,2154,2157,2159,2161,2163],{"class":402,"line":2153},68,[400,2155,2156],{"class":410},"             .sort(",[400,2158,872],{"class":443},[400,2160,217],{"class":410},[400,2162,477],{"class":443},[400,2164,2165],{"class":410},"))\n",[400,2167,2169,2172,2175,2178,2182,2185,2188,2191,2193,2196,2198,2201],{"class":402,"line":2168},69,[400,2170,2171],{"class":410},"    mo.vstack([mo.md(",[400,2173,2174],{"class":406},"f",[400,2176,2177],{"class":443},"\"**",[400,2179,2181],{"class":2180},"sxsTv","{",[400,2183,2184],{"class":410},"under.height",[400,2186,2187],{"class":2180},"}",[400,2189,2190],{"class":443},"** category-months under ",[400,2192,2181],{"class":2180},[400,2194,2195],{"class":410},"floor.value",[400,2197,2187],{"class":2180},[400,2199,2200],{"class":443},"%\"",[400,2202,2203],{"class":410},"), mo.ui.table(under)])\n",[400,2205,2207],{"class":402,"line":2206},70,[400,2208,1389],{"class":406},[400,2210,2212],{"class":402,"line":2211},71,[400,2213,418],{"emptyLinePlaceholder":417},[400,2215,2217],{"class":402,"line":2216},72,[400,2218,418],{"emptyLinePlaceholder":417},[400,2220,2222,2225,2228,2231,2234],{"class":402,"line":2221},73,[400,2223,2224],{"class":406},"if",[400,2226,2227],{"class":462}," __name__",[400,2229,2230],{"class":406}," ==",[400,2232,2233],{"class":443}," \"__main__\"",[400,2235,1051],{"class":410},[400,2237,2239],{"class":402,"line":2238},74,[400,2240,2241],{"class":410},"    app.run()\n",[14,2243,2244],{},"Moving the slider or changing the category recomputes only the cells that depend on\nthem: the data is read once, when the page opens. The months of the sample data show\ntheir seasons; with the threshold at 30%, the table lists the months to look into.",[14,2246,2247],{},"What you see, with the sample data (the pictures at the top of this page):",[1182,2249,2250,2278,2287,2293],{},[228,2251,2252,2253,2256,2257,2260,2261,90,2264,2267,2268,217,2271,90,2274,2277],{},"The app opens on ",[26,2254,2255],{},"All"," categories and the ",[26,2258,2259],{},"last 12 months",": one line per\ncategory, and the dashed threshold at 30%. ",[40,2262,2263],{},"Insaccati cotti",[40,2265,2266],{},"Insaccati da\ncuocere"," run around or above it; ",[40,2269,2270],{},"Salami",[40,2272,2273],{},"Salumi stagionati",[40,2275,2276],{},"Prosciutti"," run\nbelow, so the table under the chart holds their months.",[228,2279,2280,2281,2284,2285,254],{},"Move ",[26,2282,2283],{},"Flag margins under (%)"," to 25: the dashed line drops, and the count above the\ntable falls to the months of the two lowest categories and a few of ",[40,2286,2270],{},[228,2288,2280,2289,2292],{},[26,2290,2291],{},"Last months"," to 24: the chart shows two years, and you can compare each month\nwith the same month of the year before.",[228,2294,2295,2296,2298,2299,2302],{},"Choose ",[26,2297,2276],{}," in ",[26,2300,2301],{},"Category",": one line, and the table holds only its months.",[14,2304,2305,2306,2308,2309,2311],{},"In marimo's editor (",[26,2307,93],{},") the same cells show their code with their output under\nit; change the threshold's default to 25, ",[26,2310,239],{},", and the app opens on 25% for\nwhoever opens it next.",[14,2313,2314,2315,2318,2319,2322],{},"A second notebook can start from the catalogue instead of a known name, for example a\ncell that shows ",[103,2316,2317],{},"mo.ui.table(tabularia.datasources())"," and a dropdown built from its\n",[103,2320,2321],{},"path"," column.",[49,2324,2326],{"id":2325},"permissions","Permissions",[54,2328,2329,2339],{},[57,2330,2331],{},[60,2332,2333,2336],{},[63,2334,2335],{},"To",[63,2337,2338],{},"You need",[68,2340,2341,2349,2363,2370,2377,2385,2393,2401],{},[60,2342,2343,2346],{},[73,2344,2345],{},"see the notebook in Explore and in the search, open its page and the app",[73,2347,2348],{},"VIEW on its folder, and the use of its environment (being in one of the environment's groups)",[60,2350,2351,2360],{},[73,2352,2353,2354,2356,2357,2359],{},"open the editor, ",[26,2355,239],{},", change the source on the page, ",[26,2358,202],{},", discard the draft",[73,2361,2362],{},"EDIT on its folder",[60,2364,2365,2368],{},[73,2366,2367],{},"stop the app, or someone else's editor",[73,2369,2362],{},[60,2371,2372,2375],{},[73,2373,2374],{},"set how many people may have the app open at once",[73,2376,2362],{},[60,2378,2379,2382],{},[73,2380,2381],{},"create, rename, duplicate, delete",[73,2383,2384],{},"EDIT on the folder",[60,2386,2387,2390],{},[73,2388,2389],{},"move",[73,2391,2392],{},"EDIT on both folders",[60,2394,2395,2398],{},[73,2396,2397],{},"what the app reads",[73,2399,2400],{},"what the author of the current version may read (VIEW), checked at every read",[60,2402,2403,2406],{},[73,2404,2405],{},"what the editor reads",[73,2407,2408],{},"what the editor may read, checked at every read",[14,2410,2411],{},"Whoever lacks the folder's VIEW or the environment's use does not find the notebook, as\nfor a saved script. Administrators see every notebook and use every environment. A\nduplicate is a new notebook whose first version is by whoever made it.",[14,2413,2414,2415,254],{},"A folder that holds a notebook, and an environment a notebook uses, cannot be deleted.\nEvery create, change, restore, start, stop, save from the editor and read is in the\n",[18,2416,2418],{"href":2417},"\u002Fdocs\u002Fgovernance\u002Faudit-log","audit log",[49,2420,2422],{"id":2421},"limits-and-good-to-know","Limits and good to know",[225,2424,2425,2434,2440,2446,2452,2461,2473,2491,2553],{},[228,2426,2427,2430,2431,2433],{},[26,2428,2429],{},"Kubernetes only."," On Docker Compose there are no notebooks: ",[26,2432,81],{}," does not offer\nthem.",[228,2435,2436,2439],{},[26,2437,2438],{},"Python only",": an R environment is refused.",[228,2441,2442,2445],{},[26,2443,2444],{},"One editor at a time."," Whoever opens the editor while someone else has it sees\n«Being edited by …»; the app stays open to everyone meanwhile.",[228,2447,2448,2451],{},[26,2449,2450],{},"On demand only."," There is no «always on»: the first visit after a pause waits for\nthe pod to start.",[228,2453,2454,2455,2458,2459,254],{},"The app runs ",[26,2456,2457],{},"the current version",", never the draft: work in the editor reaches the\nviewers only with ",[26,2460,239],{},[228,2462,2463,2464,2468,2469,254],{},"A notebook reads datasources; it does not write them. To publish data, use a\n",[18,2465,2467],{"href":2466},"\u002Fdocs\u002Fscripting#saved-scripts","saved script"," or a ",[18,2470,2472],{"href":2471},"\u002Fdocs\u002Fcapabilities\u002Fflows","flow",[228,2474,2475,2476,217,2479,2482,2483,2486,2487,2490],{},"What the code downloads is held in the pod's memory and its ",[103,2477,2478],{},"\u002Ftmp",[26,2480,2481],{},"once per visitor",":\nthe environment's memory limit applies, as for a script, and an app takes a limited\nnumber of visitors at once (see ",[18,2484,787],{"href":2485},"#visitors-at-once-and-memory",").\n",[103,2488,2489],{},"columns="," reads only the columns you need.",[228,2492,2493,2494,1051,2498,217,2500,217,2503,980,2505,217,2507,217,2509,980,2511,217,2513,2516,2517,2520,2521,90,2524,2146,2527,980,2530,217,2533,217,2536,217,2539,217,2542,2544,2545,2548,2549,2552],{},"The settings are in the ",[18,2495,2497],{"href":2496},"\u002Fdocs\u002Fconfiguration#notebooks-kubernetes","configuration reference",[103,2499,993],{},[103,2501,2502],{},"NOTEBOOKS__ALLOW_SAME_ORIGIN",[103,2504,762],{},[103,2506,768],{},[103,2508,782],{},[103,2510,804],{},[103,2512,812],{},[103,2514,2515],{},"NOTEBOOKS__START_TIMEOUT_SECONDS",". With Helm they are\n",[103,2518,2519],{},"scripts.notebooks.host"," (with ",[103,2522,2523],{},"origin",[103,2525,2526],{},"tlsSecretName",[103,2528,2529],{},"allowSameOrigin",[103,2531,2532],{},"idleMinutes",[103,2534,2535],{},"maxLifetimeHours",[103,2537,2538],{},"maxLive",[103,2540,2541],{},"maxVisitorsPerApp",[103,2543,823],{},"\nand ",[103,2546,2547],{},"startTimeoutSeconds","; the chart also sets the address the pods call\n(",[103,2550,2551],{},"NOTEBOOKS__GATEWAY_URL","). The two about visitors are also in Administration →\nSettings; the address is set only at installation, since the Ingress and the frontend\nneed it too.",[228,2554,2555],{},"marimo is open-source software under the Apache-2.0 licence; its name is used here\nonly to describe the feature.",[49,2557,2559],{"id":2558},"related","Related",[225,2561,2562,2567,2574,2581,2586],{},[228,2563,2564,2566],{},[18,2565,165],{"href":164},": environments, packages, variables, network policies",[228,2568,2569,2573],{},[18,2570,2572],{"href":2571},"\u002Fdocs\u002Fsql-scripts","SQL scripts",", for questions a query answers",[228,2575,2576,2580],{},[18,2577,2579],{"href":2578},"\u002Fdocs\u002Fcapabilities\u002Fpreview-and-analysis","Preview and analysis",", the same data in the Viewer",[228,2582,2583],{},[18,2584,2326],{"href":2585},"\u002Fdocs\u002Fgovernance\u002Fpermissions",[228,2587,2588],{},[18,2589,2591],{"href":2590},"\u002Fdocs\u002Fdeploy\u002Fkubernetes","Kubernetes",[2593,2594,2595],"style",{},"html pre.shiki code .s6PUj, html code.shiki .s6PUj{--shiki-default:#F47067;--shiki-light:#D73A49}html pre.shiki code .ssh_m, html code.shiki .ssh_m{--shiki-default:#ADBAC7;--shiki-light:#24292E}html pre.shiki code .sgHix, html code.shiki .sgHix{--shiki-default:#768390;--shiki-light:#6A737D}html pre.shiki code .sXfbr, html code.shiki .sXfbr{--shiki-default:#96D0FF;--shiki-light:#032F62}html pre.shiki code .swcJU, html code.shiki .swcJU{--shiki-default:#6CB6FF;--shiki-light:#005CC5}html pre.shiki code .sNjOc, html code.shiki .sNjOc{--shiki-default:#F69D50;--shiki-light:#E36209}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 .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 pre.shiki code .shb1k, html code.shiki .shb1k{--shiki-default:#8DDB8C;--shiki-light:#22863A}html pre.shiki code .saVmf, html code.shiki .saVmf{--shiki-default:#DCBDFB;--shiki-light:#6F42C1}html pre.shiki code .sxsTv, html code.shiki .sxsTv{--shiki-default:#F47067;--shiki-light:#005CC5}",{"title":396,"searchDepth":414,"depth":421,"links":2597},[2598,2599,2611,2612,2613,2614],{"id":51,"depth":414,"text":52},{"id":174,"depth":414,"text":175,"children":2600},[2601,2602,2603,2604,2605,2606,2607,2608,2609,2610],{"id":179,"depth":421,"text":180},{"id":298,"depth":421,"text":111},{"id":328,"depth":421,"text":329},{"id":381,"depth":421,"text":382},{"id":537,"depth":421,"text":538},{"id":747,"depth":421,"text":748},{"id":786,"depth":421,"text":787},{"id":913,"depth":421,"text":914},{"id":954,"depth":421,"text":955},{"id":973,"depth":421,"text":170},{"id":1143,"depth":414,"text":1144},{"id":2325,"depth":414,"text":2326},{"id":2421,"depth":414,"text":2422},{"id":2558,"depth":414,"text":2559},"A notebook is a marimo notebook kept in a folder, like a flow or a\nsaved script: written in marimo's own reactive editor, saved with its versions, found in\nExplore and in the search, and shared through the folder's permissions. Whoever can see\nit opens it as a web app, with its sliders, dropdowns, tables and charts, without\nseeing or touching the code. The notebook reads Tabularia's datasources when its code\nasks for them, with the rights of its author, and its pod runs only while somebody uses\nit.","md",{"source":2618},"docs\u002Fcapabilities\u002Fnotebooks.md","\u002Fdocs\u002Fcapabilities\u002Fnotebooks",{"title":5,"description":2615},"docs\u002Fcapabilities\u002Fnotebooks","Fb2jjkWwV6IFQtZfxUFQ20-FiOqzo6YnEiLvZBBsOGI",[2624,2641,2686,2718,2750,2777,2800,2824],{"title":2625,"icon":1582,"intro":2626,"items":2627},"Getting started","Install it on one machine, learn the few ideas everything else is built on, and run a first flow.",[2628,2630,2633,2635,2638],{"path":2629,"title":2625},"\u002Fdocs\u002Fgetting-started",{"path":2631,"title":2632},"\u002Fdocs\u002Fgetting-started-docker-compose","Walkthrough with Docker Compose",{"path":1111,"title":2634},"Try it on your computer (minikube)",{"path":2636,"title":2637},"\u002Fdocs\u002Fconcepts","Concepts",{"path":2639,"title":2640},"\u002Fdocs\u002Foverview","Overview (README)",{"title":2642,"icon":2643,"intro":2644,"items":2645},"Capabilities","capabilities","What you can build: visual flows, analysis on any step, AI questions, pixel-perfect reports, dashboards, Python, R and SQL scripts, containers and the orchestration that ties them together.",[2646,2649,2651,2652,2655,2657,2660,2663,2664,2665,2666,2669,2672,2675,2678,2680,2683],{"path":2647,"title":2648},"\u002Fdocs\u002Fcapabilities","All capabilities",{"path":2471,"title":2650},"Low-code flows",{"path":2578,"title":2579},{"path":2653,"title":2654},"\u002Fdocs\u002Fcapabilities\u002Fask-your-data","Ask your data with AI",{"path":544,"title":2656},"Pixel-perfect reports",{"path":2658,"title":2659},"\u002Fdocs\u002Fcapabilities\u002Freport-functions","Report functions",{"path":2661,"title":2662},"\u002Fdocs\u002Fcapabilities\u002Fdashboards","Dashboards",{"path":164,"title":165},{"path":2571,"title":2572},{"path":2619,"title":5},{"path":2667,"title":2668},"\u002Fdocs\u002Fcontainer-node","Docker images in flows",{"path":2670,"title":2671},"\u002Fdocs\u002Fcapabilities\u002Fflow-groups","Flow groups: orchestration",{"path":2673,"title":2674},"\u002Fdocs\u002Fcapabilities\u002Fscheduling","Scheduling",{"path":2676,"title":2677},"\u002Fdocs\u002Fpushmail","Pushmail: datasources, reports and dashboards by email",{"path":699,"title":2679},"Dynamic dates",{"path":2681,"title":2682},"\u002Fdocs\u002Fcapabilities\u002Fengines","Engines",{"path":2684,"title":2685},"\u002Fdocs\u002Fengines\u002Fengine-differences","Engine differences",{"title":2687,"icon":2688,"intro":2689,"items":2690},"Sources","sources","Every way data comes in: files, databases, buckets, SFTP folders, HTTP APIs, SharePoint, Google Sheets, and the results of your own flows.",[2691,2694,2697,2700,2703,2706,2709,2712,2715],{"path":2692,"title":2693},"\u002Fdocs\u002Fsources","All sources",{"path":2695,"title":2696},"\u002Fdocs\u002Fsources\u002Ffiles","Uploaded files",{"path":2698,"title":2699},"\u002Fdocs\u002Fsources\u002Fdatabases","Database tables and queries",{"path":2701,"title":2702},"\u002Fdocs\u002Fs3-datasource","S3 buckets",{"path":2704,"title":2705},"\u002Fdocs\u002Fsftp","SFTP folders",{"path":2707,"title":2708},"\u002Fdocs\u002Fapi-datasource","HTTP APIs",{"path":2710,"title":2711},"\u002Fdocs\u002Fsources\u002Fsharepoint","Excel on SharePoint",{"path":2713,"title":2714},"\u002Fdocs\u002Fsources\u002Fgoogle-sheets","Google Sheets",{"path":2716,"title":2717},"\u002Fdocs\u002Fsources\u002Fflow-outputs","Results of flows and scripts",{"title":2719,"icon":2720,"intro":2721,"items":2722},"Connections","connections","The systems Tabularia reads from and writes to, how each connection is set up, and who may use it.",[2723,2726,2729,2732,2735,2737,2738,2740,2741,2744,2747],{"path":2724,"title":2725},"\u002Fdocs\u002Fconnections","All connections",{"path":2727,"title":2728},"\u002Fdocs\u002Fconnections\u002Fdatabases","Databases",{"path":2730,"title":2731},"\u002Fdocs\u002Fsqlserver","Microsoft SQL Server driver",{"path":2733,"title":2734},"\u002Fdocs\u002Fconnections\u002Fs3","S3 and compatible storage",{"path":2704,"title":2736},"SFTP servers",{"path":2707,"title":2708},{"path":2710,"title":2739},"SharePoint",{"path":2713,"title":2714},{"path":2742,"title":2743},"\u002Fdocs\u002Fconnections\u002Fsmtp","SMTP email",{"path":2745,"title":2746},"\u002Fdocs\u002Fcontainer-registries","Container registries",{"path":2748,"title":2749},"\u002Fdocs\u002Fconnections\u002Fdestinations","Writing data out",{"title":2751,"icon":2752,"intro":2753,"items":2754},"Integrations","integrations","How Tabularia fits your stack: AI providers, the REST API and webhooks, dbt, OpenLineage, single sign-on, SIEM and monitoring.",[2755,2758,2761,2764,2767,2768,2771,2774],{"path":2756,"title":2757},"\u002Fdocs\u002Fintegrations","All integrations",{"path":2759,"title":2760},"\u002Fdocs\u002Fintegrations\u002Fai","AI: where it is used",{"path":2762,"title":2763},"\u002Fdocs\u002Fapi","API, tokens and webhooks",{"path":2765,"title":2766},"\u002Fdocs\u002Fdbt-export","Export to dbt",{"path":528,"title":529},{"path":2769,"title":2770},"\u002Fdocs\u002Fsso-keycloak","Single sign-on with Keycloak",{"path":2772,"title":2773},"\u002Fdocs\u002Fsiem-splunk","Audit log to Splunk",{"path":2775,"title":2776},"\u002Fdocs\u002Fintegrations\u002Fmonitoring","Monitoring",{"title":2778,"icon":2779,"intro":2780,"items":2781},"Security & governance","governance","Who may see and do what, what is recorded, and how data quality and personal data are kept in check.",[2782,2785,2786,2788,2791,2794,2797],{"path":2783,"title":2784},"\u002Fdocs\u002Fgovernance","Overview",{"path":2585,"title":2326},{"path":2417,"title":2787},"Audit log",{"path":2789,"title":2790},"\u002Fdocs\u002Fgovernance\u002Fdata-contracts","Data contracts",{"path":2792,"title":2793},"\u002Fdocs\u002Fdesign\u002Fsso-group-mapping","SSO and group mapping",{"path":2795,"title":2796},"\u002Fdocs\u002Fexamples\u002Fkeycloak","Keycloak example on a laptop",{"path":2798,"title":2799},"\u002Fdocs\u002Fengines\u002Fclickhouse-ai-user","A ClickHouse user for the AI assistant",{"title":2801,"icon":2802,"intro":2803,"items":2804},"Install & operate","operate","Run it in production on Kubernetes, configure it, upgrade it and keep it healthy.",[2805,2807,2809,2812,2815,2818,2821],{"path":2590,"title":2806},"Kubernetes and Helm",{"path":1107,"title":2808},"A company-like cluster on one computer (kind)",{"path":2810,"title":2811},"\u002Fdocs\u002Fdeploy\u002Fupgrading","Upgrading, backups and restore",{"path":2813,"title":2814},"\u002Fdocs\u002Fdeploy\u002Frelease-checklist","Install and upgrade checklist",{"path":2816,"title":2817},"\u002Fdocs\u002Fconfiguration","Configuration reference",{"path":2819,"title":2820},"\u002Fdocs\u002Fengine-settings","Settings from the panel",{"path":2822,"title":2823},"\u002Fdocs\u002Fdatabase-migrations","Database migrations",{"title":2825,"icon":2826,"intro":2827,"items":2828},"Reference","reference","Release process and the remaining reference pages.",[2829],{"path":2830,"title":2831},"\u002Fdocs\u002Frelease-process","Release process",1793656371698]