[{"data":1,"prerenderedAt":745},["ShallowReactive",2],{"doc-\u002Fen\u002Fdeployment":3,"docnav-en":158},{"id":4,"title":5,"body":6,"description":150,"extension":151,"meta":152,"nav":78,"navigation":153,"path":154,"seo":155,"stem":156,"__hash__":157},"docs\u002Fen\u002Fdeployment.md","Deployment",{"type":7,"value":8,"toc":144},"minimark",[9,14,28,32,35,58,105,112,116,123,127,130,140],[10,11,13],"h2",{"id":12},"winget-requires-https","winget requires HTTPS",[15,16,17,18,22,23,27],"p",{},"A winget REST source must be served over ",[19,20,21],"strong",{},"HTTPS with a trusted certificate"," (this also applies to\n",[24,25,26],"code",{},"localhost"," on recent winget versions). Plan TLS for every endpoint clients talk to.",[10,29,31],{"id":30},"origin-via-docker","Origin via Docker",[15,33,34],{},"The Community stack is app + PostgreSQL. Two reverse-proxy options ship out of the box:",[36,37,38,49],"ul",{},[39,40,41,44,45,48],"li",{},[19,42,43],{},"Caddy"," (",[24,46,47],{},"docker-compose.yml",") — automatic Let's Encrypt for a public domain.",[39,50,51,44,54,57],{},[19,52,53],{},"Existing Traefik",[24,55,56],{},"docker-compose.traefik.yml",") — no Caddy; the app joins Traefik's network\nand is routed by labels. Use this when port 80\u002F443 is already taken by Traefik.",[59,60,65],"pre",{"className":61,"code":62,"language":63,"meta":64,"style":64},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# behind an existing Traefik:\ndocker compose -f docker-compose.traefik.yml --env-file .env.deploy up -d\n","bash","",[24,66,67,76],{"__ignoreMap":64},[68,69,72],"span",{"class":70,"line":71},"line",1,[68,73,75],{"class":74},"sHwdD","# behind an existing Traefik:\n",[68,77,79,83,87,90,93,96,99,102],{"class":70,"line":78},2,[68,80,82],{"class":81},"sBMFI","docker",[68,84,86],{"class":85},"sfazB"," compose",[68,88,89],{"class":85}," -f",[68,91,92],{"class":85}," docker-compose.traefik.yml",[68,94,95],{"class":85}," --env-file",[68,97,98],{"class":85}," .env.deploy",[68,100,101],{"class":85}," up",[68,103,104],{"class":85}," -d\n",[15,106,107,108,111],{},"Set ",[24,109,110],{},"KVELLMAN_IMAGE"," to a prebuilt image (built elsewhere and pushed to a registry) to avoid\nbuilding on a small server; migrations run automatically at container start.",[10,113,115],{"id":114},"edge-mirror-nodes-enterprise","Edge \u002F mirror nodes (Enterprise)",[15,117,118,119,122],{},"A site can run a headless ",[19,120,121],{},"edge node"," that serves the winget API and installer downloads locally,\ncaching from the origin on demand. Clients add the node as their source; the first install pulls\nthrough the node, the rest hit the LAN. Nodes enroll with the origin (one-time key → durable mTLS\nidentity) and are centrally controlled (package scope, installer filter, push\u002Fpre-stage, health).",[10,124,126],{"id":125},"telemetry-toggles","Telemetry & toggles",[15,128,129],{},"Repository-usage telemetry (searches, manifest fetches, downloads) is off by default and enabled via\nenvironment. The same pattern applies to the upstream-catalog sync and a GitHub token for imports.",[131,132,133],"blockquote",{},[15,134,135,136,139],{},"For the full, step-by-step guide (origin, edge node under WSL, TLS for LAN\u002Flocalhost, the registry\nworkflow) see ",[24,137,138],{},"DEPLOYMENT.md"," in the open-core repository.",[141,142,143],"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);}",{"title":64,"searchDepth":78,"depth":78,"links":145},[146,147,148,149],{"id":12,"depth":78,"text":13},{"id":30,"depth":78,"text":31},{"id":114,"depth":78,"text":115},{"id":125,"depth":78,"text":126},"Deploy the origin with Docker behind Caddy or an existing Traefik, plus edge nodes.","md",{},true,"\u002Fen\u002Fdeployment",{"title":5,"description":150},"en\u002Fdeployment","bkkfra33ona6FLJByyaPAXxM4d1aUfGtARgD9iJ6v08",[159,355,440,628],{"id":160,"title":161,"body":162,"description":349,"extension":151,"meta":350,"nav":71,"navigation":153,"path":351,"seo":352,"stem":353,"__hash__":354},"docs\u002Fen\u002Fgetting-started.md","Getting started",{"type":7,"value":163,"toc":342},[164,168,175,179,186,254,261,265,276,280,287,309,312,316,339],[10,165,167],{"id":166},"what-is-kvellman","What is kvellman?",[15,169,170,171,174],{},"kvellman is a self-hosted, ",[19,172,173],{},"winget-compatible"," package repository. You register it as an\nadditional winget source — the winget CLI queries it exactly like the official Microsoft source.\nNo client modifications, no custom installers.",[10,176,178],{"id":177},"_1-run-the-origin-docker","1. Run the origin (Docker)",[15,180,181,182,185],{},"The Community edition runs from a single ",[24,183,184],{},"docker compose"," stack (app + PostgreSQL). With a domain\npointing at your host and ports 80\u002F443 open:",[59,187,189],{"className":61,"code":188,"language":63,"meta":64,"style":64},"git clone \u003Copen-core-repo> kvellman && cd kvellman\ncp .env.deploy.example .env.deploy   # set DOMAIN, NUXT_SESSION_PASSWORD, POSTGRES_PASSWORD\ndocker compose --env-file .env.deploy up -d\n",[24,190,191,226,239],{"__ignoreMap":64},[68,192,193,196,199,203,206,210,213,216,219,223],{"class":70,"line":71},[68,194,195],{"class":81},"git",[68,197,198],{"class":85}," clone",[68,200,202],{"class":201},"sMK4o"," \u003C",[68,204,205],{"class":85},"open-core-rep",[68,207,209],{"class":208},"sTEyZ","o",[68,211,212],{"class":201},">",[68,214,215],{"class":85}," kvellman",[68,217,218],{"class":201}," &&",[68,220,222],{"class":221},"s2Zo4"," cd",[68,224,225],{"class":85}," kvellman\n",[68,227,228,231,234,236],{"class":70,"line":78},[68,229,230],{"class":81},"cp",[68,232,233],{"class":85}," .env.deploy.example",[68,235,98],{"class":85},[68,237,238],{"class":74},"   # set DOMAIN, NUXT_SESSION_PASSWORD, POSTGRES_PASSWORD\n",[68,240,242,244,246,248,250,252],{"class":70,"line":241},3,[68,243,82],{"class":81},[68,245,86],{"class":85},[68,247,95],{"class":85},[68,249,98],{"class":85},[68,251,101],{"class":85},[68,253,104],{"class":85},[15,255,256,257,260],{},"Database migrations run automatically on start. Open ",[24,258,259],{},"https:\u002F\u002Fyour-domain"," to create the first\nadmin account.",[10,262,264],{"id":263},"_2-provide-a-package","2. Provide a package",[15,266,267,268,271,272,275],{},"In the web UI: create a ",[19,269,270],{},"site token"," (Admin → Site tokens), then ",[19,273,274],{},"upload a manifest"," or import\none from the upstream winget catalog. Optionally store the installer locally so it is served from\nyour origin.",[10,277,279],{"id":278},"_3-add-the-source-on-a-client","3. Add the source on a client",[15,281,282,283,286],{},"winget requires ",[19,284,285],{},"HTTPS"," for a REST source. On a Windows client (PowerShell):",[59,288,292],{"className":289,"code":290,"language":291,"meta":64,"style":64},"language-powershell shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","winget source add --name kvellman --type Microsoft.Rest --arg \"https:\u002F\u002Fyour-domain\u002Fapi\u002F\u003Csite-token>\"\nwinget search --source kvellman \u003Cterm>\nwinget install --source kvellman \u003CPackage.Identifier>\n","powershell",[24,293,294,299,304],{"__ignoreMap":64},[68,295,296],{"class":70,"line":71},[68,297,298],{},"winget source add --name kvellman --type Microsoft.Rest --arg \"https:\u002F\u002Fyour-domain\u002Fapi\u002F\u003Csite-token>\"\n",[68,300,301],{"class":70,"line":78},[68,302,303],{},"winget search --source kvellman \u003Cterm>\n",[68,305,306],{"class":70,"line":241},[68,307,308],{},"winget install --source kvellman \u003CPackage.Identifier>\n",[15,310,311],{},"That's it — clients now install internal software through your own winget source.",[10,313,315],{"id":314},"next-steps","Next steps",[36,317,318,325,332],{},[39,319,320,324],{},[321,322,5],"a",{"href":323},"\u002Fdocs\u002Fdeployment"," — Docker, reverse proxies (Caddy\u002FTraefik), edge nodes, TLS.",[39,326,327,331],{},[321,328,330],{"href":329},"\u002Fdocs\u002Farchitecture","Architecture"," — how the winget API, site tokens and mirroring fit together.",[39,333,334,338],{},[321,335,337],{"href":336},"\u002Fdocs\u002Feditions-licensing","Editions & licensing"," — Community vs Enterprise.",[141,340,341],{},"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 .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":64,"searchDepth":78,"depth":78,"links":343},[344,345,346,347,348],{"id":166,"depth":78,"text":167},{"id":177,"depth":78,"text":178},{"id":263,"depth":78,"text":264},{"id":278,"depth":78,"text":279},{"id":314,"depth":78,"text":315},"Run a kvellman origin and add it as a winget source in minutes.",{},"\u002Fen\u002Fgetting-started",{"title":161,"description":349},"en\u002Fgetting-started","kK4dTunMMifwqdzNjiI5Y6UPFNawYYWerxVCiGSe-fI",{"id":4,"title":5,"body":356,"description":150,"extension":151,"meta":438,"nav":78,"navigation":153,"path":154,"seo":439,"stem":156,"__hash__":157},{"type":7,"value":357,"toc":432},[358,360,366,368,370,384,410,414,416,420,422,424,430],[10,359,13],{"id":12},[15,361,17,362,22,364,27],{},[19,363,21],{},[24,365,26],{},[10,367,31],{"id":30},[15,369,34],{},[36,371,372,378],{},[39,373,374,44,376,48],{},[19,375,43],{},[24,377,47],{},[39,379,380,44,382,57],{},[19,381,53],{},[24,383,56],{},[59,385,386],{"className":61,"code":62,"language":63,"meta":64,"style":64},[24,387,388,392],{"__ignoreMap":64},[68,389,390],{"class":70,"line":71},[68,391,75],{"class":74},[68,393,394,396,398,400,402,404,406,408],{"class":70,"line":78},[68,395,82],{"class":81},[68,397,86],{"class":85},[68,399,89],{"class":85},[68,401,92],{"class":85},[68,403,95],{"class":85},[68,405,98],{"class":85},[68,407,101],{"class":85},[68,409,104],{"class":85},[15,411,107,412,111],{},[24,413,110],{},[10,415,115],{"id":114},[15,417,118,418,122],{},[19,419,121],{},[10,421,126],{"id":125},[15,423,129],{},[131,425,426],{},[15,427,135,428,139],{},[24,429,138],{},[141,431,143],{},{"title":64,"searchDepth":78,"depth":78,"links":433},[434,435,436,437],{"id":12,"depth":78,"text":13},{"id":30,"depth":78,"text":31},{"id":114,"depth":78,"text":115},{"id":125,"depth":78,"text":126},{},{"title":5,"description":150},{"id":441,"title":337,"body":442,"description":622,"extension":151,"meta":623,"nav":241,"navigation":153,"path":624,"seo":625,"stem":626,"__hash__":627},"docs\u002Fen\u002Feditions-licensing.md",{"type":7,"value":443,"toc":617},[444,448,463,539,547,551,558,593,596,600,603],[10,445,447],{"id":446},"open-core","Open core",[15,449,450,451,454,455,458,459,462],{},"kvellman is ",[19,452,453],{},"open core",". The platform is open source under ",[19,456,457],{},"Apache-2.0","; advanced features ship\nas ",[19,460,461],{},"commercial plugins"," (e.g. SSO, MFA) that activate only with a valid Enterprise license.",[464,465,466,481],"table",{},[467,468,469],"thead",{},[470,471,472,475,478],"tr",{},[473,474],"th",{},[473,476,477],{},"Community",[473,479,480],{},"Enterprise",[482,483,484,495,506,517,528],"tbody",{},[470,485,486,490,492],{},[487,488,489],"td",{},"License",[487,491,457],{},[487,493,494],{},"Commercial subscription",[470,496,497,500,503],{},[487,498,499],{},"Users",[487,501,502],{},"Single \u002F local accounts",[487,504,505],{},"Multi-user, RBAC",[470,507,508,511,514],{},[487,509,510],{},"Auth",[487,512,513],{},"Local",[487,515,516],{},"SSO (OIDC) + MFA (TOTP)",[470,518,519,522,525],{},[487,520,521],{},"Scale",[487,523,524],{},"Single node",[487,526,527],{},"High availability, edge nodes",[470,529,530,533,536],{},[487,531,532],{},"Operations",[487,534,535],{},"Core",[487,537,538],{},"Approval workflow + audit, air-gapped, release scraper",[15,540,541,542,546],{},"See ",[321,543,545],{"href":544},"\u002Fpricing","pricing"," for the full comparison.",[10,548,550],{"id":549},"how-licensing-works","How licensing works",[15,552,553,554,557],{},"Enterprise features are gated by an ",[19,555,556],{},"offline, signature-based"," license — no activation server, so\nit works air-gapped:",[559,560,561,568,575,590],"ol",{},[39,562,563,564,567],{},"A license is an ",[19,565,566],{},"Ed25519-signed token"," issued by the vendor.",[39,569,570,571,574],{},"The product ships only the ",[19,572,573],{},"public key"," and verifies the token locally.",[39,576,577,578,581,582,585,586,589],{},"The token lists the customer and the active ",[19,579,580],{},"entitlements"," (e.g. ",[24,583,584],{},"sso",", ",[24,587,588],{},"mfa",") and an expiry.",[39,591,592],{},"An admin pastes the token in the UI; matching plugins activate immediately — no restart, no\ninternet required.",[15,594,595],{},"Because verification is local, even the full open-source code cannot forge a valid license without\nthe vendor's private key.",[10,597,599],{"id":598},"developer-packages","Developer packages",[15,601,602],{},"The shared, public building blocks are on npm under Apache-2.0:",[36,604,605,611],{},[39,606,607,610],{},[24,608,609],{},"@kvellman\u002Fwinget-contract"," — the winget REST\u002Ftypes contract shared by origin and nodes.",[39,612,613,616],{},[24,614,615],{},"@kvellman\u002Fplugin-sdk"," — types for building auth\u002Fentitlement plugins.",{"title":64,"searchDepth":78,"depth":78,"links":618},[619,620,621],{"id":446,"depth":78,"text":447},{"id":549,"depth":78,"text":550},{"id":598,"depth":78,"text":599},"Open-core model — Community (Apache-2.0) vs Enterprise, and how licensing works.",{},"\u002Fen\u002Feditions-licensing",{"title":337,"description":622},"en\u002Feditions-licensing","GG0PkuJPsApFCUuFyP9GP_yXQMJjDVLXffyEuQ3kbmI",{"id":629,"title":330,"body":630,"description":738,"extension":151,"meta":739,"nav":740,"navigation":153,"path":741,"seo":742,"stem":743,"__hash__":744},"docs\u002Fen\u002Farchitecture.md",{"type":7,"value":631,"toc":731},[632,636,639,659,663,694,698,717,721,724,728],[10,633,635],{"id":634},"winget-source-api","winget Source API",[15,637,638],{},"kvellman implements the winget Source REST contract exactly, so the winget CLI treats it like any\nother source:",[36,640,641,647,653],{},[39,642,643,646],{},[24,644,645],{},"GET \u002Fapi\u002F{siteToken}\u002Finformation"," — source metadata and supported versions.",[39,648,649,652],{},[24,650,651],{},"POST \u002Fapi\u002F{siteToken}\u002FmanifestSearch"," — full-text search.",[39,654,655,658],{},[24,656,657],{},"GET \u002Fapi\u002F{siteToken}\u002FpackageManifests\u002F{id}"," — manifest delivery with server-side resolution.",[10,660,662],{"id":661},"site-context","Site context",[15,664,665,666,668,669,672,673,676,677,680,681,585,684,585,687,585,690,693],{},"winget sends no custom headers, so site context is carried in the URL ",[19,667,270],{}," (primary),\nwith ",[19,670,671],{},"subnet"," and ",[19,674,675],{},"mTLS client certificate"," as additional signals. The resolved context drives\nserver-side ",[24,678,679],{},"InstallerUrl"," placeholders (",[24,682,683],{},"$REPO_URL",[24,685,686],{},"$SITE",[24,688,689],{},"$LOCATION",[24,691,692],{},"$LANG",") — so the same\nmanifest can point at a site-local SMB\u002FDFS share without being forked.",[10,695,697],{"id":696},"manifests-overlays-storage","Manifests, overlays & storage",[15,699,700,701,704,705,708,709,712,713,716],{},"Manifests are versioned YAML, validated against the official winget JSON schemas on upload. Local\nedits are kept as an ",[19,702,703],{},"overlay"," on top of the untouched upstream, and per-package ",[19,706,707],{},"overlay\ntemplates"," make repeatable edits (e.g. an internal mirror URL with ",[24,710,711],{},"$VERSION","\u002F",[24,714,715],{},"$ARCH",") easy across\nversions. Installers can be mirrored to S3\u002FMinIO or SMB; hashes are recomputed on delivery.",[10,718,720],{"id":719},"edge-mirror-nodes","Edge \u002F mirror nodes",[15,722,723],{},"Edge nodes are thin caching proxies that serve the winget API + downloads locally and lazily fetch\nfrom the origin. They keep an offline search index, enroll via mTLS, and are centrally scoped\n(which packages, which installer architectures\u002Fscopes) and observable from the origin.",[10,725,727],{"id":726},"stack","Stack",[15,729,730],{},"Nuxt 4 (Vue 3 + Nitro), TypeScript, PostgreSQL + Drizzle ORM, S3-compatible storage, BullMQ\u002FRedis\nfor background jobs. On-premise only — fully self-hostable, including air-gapped operation.",{"title":64,"searchDepth":78,"depth":78,"links":732},[733,734,735,736,737],{"id":634,"depth":78,"text":635},{"id":661,"depth":78,"text":662},{"id":696,"depth":78,"text":697},{"id":719,"depth":78,"text":720},{"id":726,"depth":78,"text":727},"How the winget Source API, site context, mirroring and edge nodes fit together.",{},4,"\u002Fen\u002Farchitecture",{"title":330,"description":738},"en\u002Farchitecture","Eqw1cDRa0-CLXwr65192sIi5s3k47eNKWIDs5A9qIiE",1782195177434]