[{"data":1,"prerenderedAt":595},["ShallowReactive",2],{"docs-page:en:\u002Fdocs\u002Fbuild\u002Fgenerate-bundle":3,"docs-navigation:en":498},{"id":4,"title":5,"body":6,"description":490,"extension":491,"meta":492,"navigation":493,"path":494,"seo":495,"stem":496,"__hash__":497},"docs_en\u002Fen\u002Fdocs\u002Fbuild\u002Fgenerate-bundle.md","Generate a bundle",{"type":7,"value":8,"toc":472},"minimark",[9,13,16,32,45,50,53,82,85,95,98,102,105,110,113,143,158,162,165,168,175,182,192,199,202,206,209,228,232,238,241,243,271,274,285,289,292,295,333,336,339,342,345,349,352,372,376,379,382,388,390,396,399,403,406,434,438,441,465,468],[10,11,12],"p",{},"Otalan accepts one release artifact shape in the dashboard: a ZIP archive that contains the web or Expo-exported assets for a single release target.",[10,14,15],{},"This page explains how to prepare that artifact cleanly so Otalan can validate and activate it without surprises.",[10,17,18,19,23,24,27,28,31],{},"For dashboard UI publishing, the recommended path is still to run ",[20,21,22],"code",{},"otalan bundle"," in the app repository and upload the generated ",[20,25,26],{},".otalan\u002Fbundle\u002Fbundle-\u003Cbundle-id>.zip"," file. For Expo releases, keep the generated ",[20,29,30],{},".otalan\u002Fbundle\u002Fmanifest.json"," ready for the manifest field too.",[10,33,34,37,38,41,42,44],{},[20,35,36],{},".otalan\u002F"," is generated output. Add it to ",[20,39,40],{},".gitignore","; publish reads it from the current local or CI workspace after ",[20,43,22],{}," runs.",[46,47,49],"h2",{"id":48},"define-the-release-inputs-first","Define the release inputs first",[10,51,52],{},"Before you package anything, decide the tuple and release identifiers you are targeting:",[54,55,56,62,67,72,77],"ul",{},[57,58,59],"li",{},[20,60,61],{},"appId",[57,63,64],{},[20,65,66],{},"platform",[57,68,69],{},[20,70,71],{},"channel",[57,73,74],{},[20,75,76],{},"runtimeVersion",[57,78,79],{},[20,80,81],{},"bundleId",[10,83,84],{},"Example:",[86,87,93],"pre",{"className":88,"code":90,"language":91,"meta":92},[89],"language-text","appId         = com.company.app1\nplatform      = ios\nchannel       = production\nruntimeVersion = 3.4.0\nbundleId      = 3.4.0-web.5\n","text","",[20,94,90],{"__ignoreMap":92},[10,96,97],{},"The archive itself does not encode all of these values. They become meaningful only when you publish the archive through the Otalan release flow.",[46,99,101],{"id":100},"build-the-frontend-output","Build the frontend output",[10,103,104],{},"Otalan does not build your application for you. You must first run the build or export step that produces the assets the client will actually load.",[106,107,109],"h3",{"id":108},"for-capacitor-style-web-bundles","For Capacitor-style web bundles",[10,111,112],{},"Build your web app with your normal Bun workflow:",[86,114,118],{"className":115,"code":116,"language":117,"meta":92,"style":92},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# Build the Capacitor web assets.\nbun run build\n","bash",[20,119,120,129],{"__ignoreMap":92},[121,122,125],"span",{"class":123,"line":124},"line",1,[121,126,128],{"class":127},"sHwdD","# Build the Capacitor web assets.\n",[121,130,132,136,140],{"class":123,"line":131},2,[121,133,135],{"class":134},"sBMFI","bun",[121,137,139],{"class":138},"sfazB"," run",[121,141,142],{"class":138}," build\n",[10,144,145,146,149,150,153,154,157],{},"The result is usually a directory such as ",[20,147,148],{},"dist\u002F",", ",[20,151,152],{},"www\u002F",", or ",[20,155,156],{},".output\u002Fpublic\u002F",", depending on your stack.",[46,159,161],{"id":160},"keep-web-bundles-minimal","Keep web bundles minimal",[10,163,164],{},"The OTA bundle should contain the app shell, not the content library. Keep HTML, CSS, JavaScript, manifests, and small runtime-critical assets in the generated output.",[10,166,167],{},"Do not embed product images, videos, large fonts, PDFs, marketing media, or other heavy files in JavaScript, CSS, base64 strings, or the ZIP itself. Host those files externally and reference them by URL so a text or layout update does not force users to redownload unrelated media.",[106,169,171,172],{"id":170},"for-expo-with-expo-updates","For Expo with ",[20,173,174],{},"expo-updates",[10,176,177,178,181],{},"Export the update artifacts using the Expo workflow your app already uses. In practice, that often means a command in the ",[20,179,180],{},"expo export"," family.",[10,183,184,185,187,188,191],{},"When publishing manually through the dashboard, choose the Expo runtime in the publish form and keep the generated Otalan ",[20,186,30],{}," ready. Paste its content or drop the ",[20,189,190],{},"manifest.json"," file onto the manifest field so Otalan can validate the Expo release metadata.",[10,193,194,195,198],{},"If you want Otalan to handle that repository-aware packaging flow for you, prefer ",[20,196,197],{},"@otalan\u002Fcli",".",[10,200,201],{},"Otalan does not block older runtime versions by version number. Official support currently covers Expo 54, 55, and 56 for Expo publish flows. Other runtimes and older Expo or Capacitor versions might work or might not, but they are not supported for the moment.",[46,203,205],{"id":204},"where-this-page-fits","Where this page fits",[10,207,208],{},"This page is about the release artifact itself: what goes into the ZIP, what stays out, and what Otalan validates before activation.",[10,210,211,212,217,218,222,223,227],{},"Use ",[213,214,216],"a",{"href":215},"\u002Fdocs\u002Fquick-start\u002Fquick-start","Publish in 5 minutes"," for the first publish path, ",[213,219,221],{"href":220},"\u002Fdocs\u002Ftooling\u002Fcli","CLI"," for automation behavior, and ",[213,224,226],{"href":225},"\u002Fdocs\u002Fdeploy\u002Fpublish-release","Publish a release"," for dashboard operation.",[46,229,231],{"id":230},"package-only-the-generated-assets","Package only the generated assets",[10,233,234,235,237],{},"For dashboard upload, use the CLI output at ",[20,236,26],{},". If you are preparing an archive outside the dashboard and CLI path, package the generated output directory into one archive.",[10,239,240],{},"Create the ZIP from the generated output directory, not from the repository root.",[10,242,84],{},[86,244,246],{"className":115,"code":245,"language":117,"meta":92,"style":92},"cd dist\nzip -r ..\u002Fotalan-bundle.zip .\n",[20,247,248,257],{"__ignoreMap":92},[121,249,250,254],{"class":123,"line":124},[121,251,253],{"class":252},"s2Zo4","cd",[121,255,256],{"class":138}," dist\n",[121,258,259,262,265,268],{"class":123,"line":131},[121,260,261],{"class":134},"zip",[121,263,264],{"class":138}," -r",[121,266,267],{"class":138}," ..\u002Fotalan-bundle.zip",[121,269,270],{"class":138}," .\n",[10,272,273],{},"The important rule is simple:",[54,275,276,279,282],{},[57,277,278],{},"include only the generated runtime assets",[57,280,281],{},"do not include the whole repository",[57,283,284],{},"do not wrap the output in an extra parent directory unless your runtime explicitly expects it",[46,286,288],{"id":287},"what-otalan-validates","What Otalan validates",[10,290,291],{},"Every uploaded ZIP is validated before activation. Otalan rejects the archive if the content is unsafe or structurally invalid.",[10,293,294],{},"Otalan checks for:",[54,296,297,303,306,309,312,315,318,321,324,327,330],{},[57,298,299,300],{},"path traversal attempts such as ",[20,301,302],{},"..\u002F",[57,304,305],{},"absolute paths",[57,307,308],{},"duplicate archive entries",[57,310,311],{},"encrypted entries",[57,313,314],{},"source maps",[57,316,317],{},"symbolic links",[57,319,320],{},"nested ZIP archives",[57,322,323],{},"archive size that exceeds configured limits",[57,325,326],{},"extracted size that exceeds configured limits",[57,328,329],{},"excessive file counts or path lengths",[57,331,332],{},"extreme compression ratios",[10,334,335],{},"Compression-ratio checks only apply once the extracted entry or archive size reaches 64 KB, so tiny optimized assets are not rejected solely for compressing well.",[10,337,338],{},"The default minimum billable ZIP size is 50 KB for analytics and quotas. Valid smaller archives are accepted and counted as 50 KB there.",[10,340,341],{},"Storage accounting is based on what Otalan keeps in managed object storage. Capacitor releases count the stored archive. Expo releases count the stored archive plus the extracted Expo launch asset and listed asset objects that Expo downloads from the manifest.",[10,343,344],{},"The archive should be boring. Boring is good here.",[46,346,348],{"id":347},"practical-packaging-rules","Practical packaging rules",[10,350,351],{},"Use these rules to keep the validation path clean:",[54,353,354,357,360,363,366,369],{},[57,355,356],{},"package compiled assets only",[57,358,359],{},"keep product media, videos, large documents, and other externally hosted content outside the bundle",[57,361,362],{},"avoid local editor artifacts",[57,364,365],{},"do not include shell scripts or installers",[57,367,368],{},"do not include another ZIP inside the bundle",[57,370,371],{},"keep bundle naming predictable and human-readable",[46,373,375],{"id":374},"recommended-bundle-naming","Recommended bundle naming",[10,377,378],{},"Use a bundle ID that helps operators understand both the runtime line and the iteration count.",[10,380,381],{},"A practical pattern is:",[86,383,386],{"className":384,"code":385,"language":91,"meta":92},[89],"\u003CruntimeVersion>-web.\u003Cincrement>\n",[20,387,385],{"__ignoreMap":92},[10,389,84],{},[86,391,394],{"className":392,"code":393,"language":91,"meta":92},[89],"3.4.0-web.5\n",[20,395,393],{"__ignoreMap":92},[10,397,398],{},"That pattern makes rollback and release history easier to understand at a glance.",[46,400,402],{"id":401},"expo-specific-notes","Expo-specific notes",[10,404,405],{},"For Expo releases, keep these points in mind:",[54,407,408,411,417,420,429],{},[57,409,410],{},"the exported assets still need to be packaged into the ZIP you publish",[57,412,413,414,416],{},"the runtime-facing manifest is served by Otalan, but ",[20,415,174],{}," still owns the client-side install flow",[57,418,419],{},"the generated manifest tells Expo where to fetch the release assets",[57,421,422,423,425,426,428],{},"upload ",[20,424,26],{},", choose Expo in the dashboard publish form, and paste the generated Otalan manifest JSON or drop the ",[20,427,190],{}," file",[57,430,431,432],{},"if you want a repeatable export-and-publish pipeline, move to ",[20,433,197],{},[46,435,437],{"id":436},"preflight-checklist","Preflight checklist",[10,439,440],{},"Before you upload, confirm:",[442,443,444,447,456,459,462],"ol",{},[57,445,446],{},"you built or exported the exact assets for the intended target",[57,448,449,450,452,453,455],{},"the dashboard ZIP is ",[20,451,26],{}," from ",[20,454,22],{}," and contains only runtime assets",[57,457,458],{},"the tuple values you plan to publish are correct",[57,460,461],{},"the bundle ID is new for that target",[57,463,464],{},"you have the generated Otalan manifest JSON ready for Expo releases",[10,466,467],{},"If those five points are true, the dashboard publish step becomes straightforward and Otalan has a clean archive to validate.",[469,470,471],"style",{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .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 .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}",{"title":92,"searchDepth":473,"depth":473,"links":474},3,[475,476,479,483,484,485,486,487,488,489],{"id":48,"depth":131,"text":49},{"id":100,"depth":131,"text":101,"children":477},[478],{"id":108,"depth":473,"text":109},{"id":160,"depth":131,"text":161,"children":480},[481],{"id":170,"depth":473,"text":482},"For Expo with expo-updates",{"id":204,"depth":131,"text":205},{"id":230,"depth":131,"text":231},{"id":287,"depth":131,"text":288},{"id":347,"depth":131,"text":348},{"id":374,"depth":131,"text":375},{"id":401,"depth":131,"text":402},{"id":436,"depth":131,"text":437},"Prepare a release artifact that will pass Otalan's validation pipeline, including the extra considerations required for Expo-based publish flows.","md",{},true,"\u002Fen\u002Fdocs\u002Fbuild\u002Fgenerate-bundle",{"title":5,"description":490},"en\u002Fdocs\u002Fbuild\u002Fgenerate-bundle","WyFHlTA42Bqy-KjRa6JqDcFZdaX9N7WAUuvEtplUZcc",[499],{"title":500,"path":501,"stem":502,"children":503,"page":521},"En","\u002Fen","en",[504],{"title":505,"path":506,"stem":507,"children":508,"page":-1,"description":510},"Introduction","\u002Fen\u002Fdocs","en\u002Fdocs\u002Findex",[509,511,522,528,537,547,566,585],{"title":505,"path":506,"stem":507,"description":510},"Understand what Otalan is, when to use it, and how the first safe OTA release flow works for Capacitor and Expo apps.",{"title":512,"path":513,"stem":514,"children":515,"page":521},"About","\u002Fen\u002Fdocs\u002Fabout","en\u002Fdocs\u002Fabout",[516],{"title":517,"path":518,"stem":519,"description":520},"Security and trust","\u002Fen\u002Fdocs\u002Fabout\u002Fsecurity-trust","en\u002Fdocs\u002Fabout\u002Fsecurity-trust","How Otalan keeps OTA releases controlled: scoped keys, bundle validation, exact matching, rollout controls, rollback, deletion behavior, and support contact.",false,{"title":523,"path":524,"stem":525,"children":526,"page":521},"Build","\u002Fen\u002Fdocs\u002Fbuild","en\u002Fdocs\u002Fbuild",[527],{"title":5,"path":494,"stem":496,"description":490},{"title":529,"path":530,"stem":531,"children":532,"page":521},"Deploy","\u002Fen\u002Fdocs\u002Fdeploy","en\u002Fdocs\u002Fdeploy",[533],{"title":226,"path":534,"stem":535,"description":536},"\u002Fen\u002Fdocs\u002Fdeploy\u002Fpublish-release","en\u002Fdocs\u002Fdeploy\u002Fpublish-release","Submit a release through the dashboard, understand each field in the publish form, and operate the validation lifecycle with confidence.",{"title":538,"path":539,"stem":540,"children":541,"page":521},"Integration","\u002Fen\u002Fdocs\u002Fintegration","en\u002Fdocs\u002Fintegration",[542],{"title":543,"path":544,"stem":545,"description":546},"Migrate from App Center CodePush","\u002Fen\u002Fdocs\u002Fintegration\u002Fapp-center-codepush","en\u002Fdocs\u002Fintegration\u002Fapp-center-codepush","Plan a migration from Microsoft App Center CodePush to Otalan, with mapping notes, rollout guidance, and common failure modes.",{"title":548,"path":549,"stem":550,"children":551,"page":521},"Quick Start","\u002Fen\u002Fdocs\u002Fquick-start","en\u002Fdocs\u002Fquick-start",[552,557,562],{"title":553,"path":554,"stem":555,"description":556},"Capacitor quick start","\u002Fen\u002Fdocs\u002Fquick-start\u002Fcapacitor","en\u002Fdocs\u002Fquick-start\u002Fcapacitor","Wire the Otalan Capacitor runtime into an installed app so it can check for OTA updates, install compatible bundles, and confirm successful launches.",{"title":558,"path":559,"stem":560,"description":561},"Expo quick start","\u002Fen\u002Fdocs\u002Fquick-start\u002Fexpo","en\u002Fdocs\u002Fquick-start\u002Fexpo","Configure expo-updates with Otalan, add the Otalan Expo helper, and make the installed app ready to receive compatible OTA bundles.",{"title":216,"path":563,"stem":564,"description":565},"\u002Fen\u002Fdocs\u002Fquick-start\u002Fquick-start","en\u002Fdocs\u002Fquick-start\u002Fquick-start","Create the first Otalan release path, publish a baseline bundle, verify one update, and prove rollback before widening rollout.",{"title":567,"path":568,"stem":569,"children":570,"page":521},"Tooling","\u002Fen\u002Fdocs\u002Ftooling","en\u002Fdocs\u002Ftooling",[571,576,580],{"title":572,"path":573,"stem":574,"description":575},"AI skill","\u002Fen\u002Fdocs\u002Ftooling\u002Fai-skill","en\u002Fdocs\u002Ftooling\u002Fai-skill","Copy a compact assistant skill for Otalan SDK setup, CLI publishing, and safe credential boundaries.",{"title":221,"path":577,"stem":578,"description":579},"\u002Fen\u002Fdocs\u002Ftooling\u002Fcli","en\u002Fdocs\u002Ftooling\u002Fcli","Learn when to use the Otalan CLI, what workflows it covers, and how it complements the dashboard instead of replacing the platform model.",{"title":581,"path":582,"stem":583,"description":584},"SDKs","\u002Fen\u002Fdocs\u002Ftooling\u002Fsdk","en\u002Fdocs\u002Ftooling\u002Fsdk","Understand the difference between the Capacitor and Expo integrations, and choose the runtime package that matches the way your mobile app actually updates.",{"title":586,"path":587,"stem":588,"children":589,"page":521},"Versions","\u002Fen\u002Fdocs\u002Fversions","en\u002Fdocs\u002Fversions",[590],{"title":591,"path":592,"stem":593,"description":594},"v1","\u002Fen\u002Fdocs\u002Fversions\u002Fv1","en\u002Fdocs\u002Fversions\u002Fv1","Supported runtimes, public limits, and post-v1 candidates for Otalan v1.",1780287525123]