[{"data":1,"prerenderedAt":1264},["ShallowReactive",2],{"navigation":3,"\u002Fgetting-started\u002Ffree-idp-hosting":105,"\u002Fgetting-started\u002Ffree-idp-hosting-surround":1259},[4,44,79,92],{"title":5,"path":6,"stem":7,"children":8,"icon":43},"Getting Started","\u002Fgetting-started","1.getting-started\u002F1.index",[9,11,15,19,23,27,31,35,39],{"title":10,"path":6,"stem":7},"Introduction",{"title":12,"path":13,"stem":14},"Quick Start: Service Provider","\u002Fgetting-started\u002Fquickstart-sp","1.getting-started\u002F2.quickstart-sp",{"title":16,"path":17,"stem":18},"Quick Start: Identity Provider","\u002Fgetting-started\u002Fquickstart-idp","1.getting-started\u002F3.quickstart-idp",{"title":20,"path":21,"stem":22},"Quick Start: Agent","\u002Fgetting-started\u002Fquickstart-agent","1.getting-started\u002F4.quickstart-agent",{"title":24,"path":25,"stem":26},"Quick Start","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F5.installation",{"title":28,"path":29,"stem":30},"How It Works","\u002Fgetting-started\u002Fhow-it-works","1.getting-started\u002F6.how-it-works",{"title":32,"path":33,"stem":34},"For Service Providers","\u002Fgetting-started\u002Ffor-service-providers","1.getting-started\u002F7.for-service-providers",{"title":36,"path":37,"stem":38},"CLI (apes & ape-shell)","\u002Fgetting-started\u002Fcli","1.getting-started\u002F8.cli",{"title":40,"path":41,"stem":42},"Free-IdP Hosting Guide","\u002Fgetting-started\u002Ffree-idp-hosting","1.getting-started\u002F9.free-idp-hosting",false,{"title":45,"path":46,"stem":47,"children":48,"icon":43},"Ecosystem","\u002Fecosystem","2.ecosystem\u002F1.index",[49,51,55,59,63,67,71,75],{"title":50,"path":46,"stem":47},"Overview",{"title":52,"path":53,"stem":54},"OpenApe Auth","\u002Fecosystem\u002Fauth","2.ecosystem\u002F2.auth",{"title":56,"path":57,"stem":58},"OpenApe Grants","\u002Fecosystem\u002Fgrants","2.ecosystem\u002F3.grants",{"title":60,"path":61,"stem":62},"nuxt-auth-sp","\u002Fecosystem\u002Fnuxt-auth-sp","2.ecosystem\u002F4.nuxt-auth-sp",{"title":64,"path":65,"stem":66},"escapes","\u002Fecosystem\u002Fescapes","2.ecosystem\u002F5.escapes",{"title":68,"path":69,"stem":70},"nuxt-auth-idp","\u002Fecosystem\u002Fnuxt-auth-idp","2.ecosystem\u002F6.nuxt-auth-idp",{"title":72,"path":73,"stem":74},"Multi-Tenant IdP","\u002Fecosystem\u002Fmulti-tenant-idp","2.ecosystem\u002F7.multi-tenant-idp",{"title":76,"path":77,"stem":78},"Agent Recipe","\u002Fecosystem\u002Fagent-recipe","2.ecosystem\u002F8.agent-recipe",{"title":80,"icon":43,"path":81,"stem":82,"children":83,"page":43},"Security","\u002Fsecurity","3.security",[84,88],{"title":85,"path":86,"stem":87},"Compliance","\u002Fsecurity\u002Fcompliance","3.security\u002F1.compliance",{"title":89,"path":90,"stem":91},"Threat Model","\u002Fsecurity\u002Fthreat-model","3.security\u002F2.threat-model",{"title":93,"icon":43,"path":94,"stem":95,"children":96,"page":43},"Guides","\u002Fguides","4.guides",[97,101],{"title":98,"path":99,"stem":100},"Capabilities & Grants","\u002Fguides\u002Fcapabilities","4.guides\u002F1.capabilities",{"title":102,"path":103,"stem":104},"Delegation","\u002Fguides\u002Fdelegation","4.guides\u002F2.delegation",{"id":106,"title":40,"body":107,"description":1253,"extension":1254,"links":1255,"meta":1256,"navigation":383,"path":41,"seo":1257,"stem":42,"__hash__":1258},"docs\u002F1.getting-started\u002F9.free-idp-hosting.md",{"type":108,"value":109,"toc":1238},"minimark",[110,114,131,137,142,196,200,226,230,235,287,291,318,529,534,570,581,585,647,651,657,722,725,765,776,797,801,816,830,834,837,902,905,909,935,938,1020,1023,1027,1030,1062,1069,1073,1079,1181,1184,1195,1212,1216,1234],[111,112,40],"h1",{"id":113},"free-idp-hosting-guide",[115,116,117,118,125,126,130],"p",{},"This guide walks through standing up a persistent, self-hosted OpenApe IdP using the reference ",[119,120,124],"a",{"href":121,"rel":122},"https:\u002F\u002Fgithub.com\u002Fopenape-ai\u002Fopenape\u002Ftree\u002Fmain\u002Fapps\u002Fopenape-free-idp",[123],"nofollow","openape-free-idp"," app — a Nuxt 4 project that wires ",[127,128,129],"code",{},"@openape\u002Fnuxt-auth-idp"," to a libsql database (either a local SQLite file or Turso).",[115,132,133,134,136],{},"If you only need a dev sandbox or want to bring your own persistence layer, see ",[119,135,24],{"href":25}," for the minimal path.",[138,139,141],"h2",{"id":140},"what-you-get","What you get",[143,144,145,149,166,179,186,193],"ul",{},[146,147,148],"li",{},"Passkey login (WebAuthn) and SSH-key agent auth out of the box.",[146,150,151,152,155,156,155,159,155,162,165],{},"OAuth\u002FOIDC endpoints (",[127,153,154],{},"\u002Fauthorize",", ",[127,157,158],{},"\u002Ftoken",[127,160,161],{},"\u002F.well-known\u002Fopenid-configuration",[127,163,164],{},"\u002Fuserinfo",", JWKS).",[146,167,168,169,155,172,155,175,178],{},"Grant approval flows at ",[127,170,171],{},"\u002Fgrants",[127,173,174],{},"\u002Fgrant-approval",[127,176,177],{},"\u002Fenroll",".",[146,180,181,182,185],{},"Admin UI at ",[127,183,184],{},"\u002Fadmin"," for users, agents, sessions, and registration URLs.",[146,187,188,189,192],{},"Optional ",[119,190,191],{"href":73},"multi-tenant hosting"," — one instance, many domains.",[146,194,195],{},"A working schema with migrations, backfills, and indexes you can iterate on.",[138,197,199],{"id":198},"prerequisites","Prerequisites",[143,201,202,205,208],{},[146,203,204],{},"Node 22+",[146,206,207],{},"pnpm 10+",[146,209,210,211],{},"A libsql target. Either:\n",[143,212,213,220],{},[146,214,215,219],{},[216,217,218],"strong",{},"Local file"," (recommended for development and single-node self-hosting) — any path on disk",[146,221,222,225],{},[216,223,224],{},"Turso"," (recommended for HA or multi-region) — a free-tier Turso DB works",[138,227,229],{"id":228},"setup","Setup",[231,232,234],"h3",{"id":233},"_1-clone-the-reference-app","1. Clone the reference app",[236,237,242],"pre",{"className":238,"code":239,"language":240,"meta":241,"style":241},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","git clone https:\u002F\u002Fgithub.com\u002Fopenape-ai\u002Fopenape\ncd openape\npnpm install\ncd apps\u002Fopenape-free-idp\n","bash","",[127,243,244,260,270,279],{"__ignoreMap":241},[245,246,249,253,257],"span",{"class":247,"line":248},"line",1,[245,250,252],{"class":251},"sBMFI","git",[245,254,256],{"class":255},"sfazB"," clone",[245,258,259],{"class":255}," https:\u002F\u002Fgithub.com\u002Fopenape-ai\u002Fopenape\n",[245,261,263,267],{"class":247,"line":262},2,[245,264,266],{"class":265},"s2Zo4","cd",[245,268,269],{"class":255}," openape\n",[245,271,273,276],{"class":247,"line":272},3,[245,274,275],{"class":251},"pnpm",[245,277,278],{"class":255}," install\n",[245,280,282,284],{"class":247,"line":281},4,[245,283,266],{"class":265},[245,285,286],{"class":255}," apps\u002Fopenape-free-idp\n",[231,288,290],{"id":289},"_2-configure-the-database","2. Configure the database",[115,292,293,294,297,298,301,302,305,306,309,310,313,314,317],{},"The database is accessed through ",[127,295,296],{},"@libsql\u002Fclient"," → ",[127,299,300],{},"drizzle-orm",". ",[127,303,304],{},"useDb()"," reads ",[127,307,308],{},"tursoUrl"," and ",[127,311,312],{},"tursoAuthToken"," from ",[127,315,316],{},"runtimeConfig",":",[236,319,323],{"className":320,"code":321,"language":322,"meta":241,"style":241},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F apps\u002Fopenape-free-idp\u002Fserver\u002Fdatabase\u002Fdrizzle.ts\nimport { createClient } from '@libsql\u002Fclient'\nimport { drizzle } from 'drizzle-orm\u002Flibsql'\n\nexport function useDb() {\n  const config = useRuntimeConfig()\n  const client = createClient({\n    url: config.tursoUrl as string,         \u002F\u002F file:.\u002Fdata\u002Fidp.db  OR  libsql:\u002F\u002F...\n    authToken: config.tursoAuthToken as string, \u002F\u002F empty for file:\u002F\u002F URLs\n  })\n  return drizzle(client, { schema })\n}\n","ts",[127,324,325,331,359,379,385,404,423,441,467,490,499,523],{"__ignoreMap":241},[245,326,327],{"class":247,"line":248},[245,328,330],{"class":329},"sHwdD","\u002F\u002F apps\u002Fopenape-free-idp\u002Fserver\u002Fdatabase\u002Fdrizzle.ts\n",[245,332,333,337,341,345,348,351,354,356],{"class":247,"line":262},[245,334,336],{"class":335},"s7zQu","import",[245,338,340],{"class":339},"sMK4o"," {",[245,342,344],{"class":343},"sTEyZ"," createClient",[245,346,347],{"class":339}," }",[245,349,350],{"class":335}," from",[245,352,353],{"class":339}," '",[245,355,296],{"class":255},[245,357,358],{"class":339},"'\n",[245,360,361,363,365,368,370,372,374,377],{"class":247,"line":272},[245,362,336],{"class":335},[245,364,340],{"class":339},[245,366,367],{"class":343}," drizzle",[245,369,347],{"class":339},[245,371,350],{"class":335},[245,373,353],{"class":339},[245,375,376],{"class":255},"drizzle-orm\u002Flibsql",[245,378,358],{"class":339},[245,380,381],{"class":247,"line":281},[245,382,384],{"emptyLinePlaceholder":383},true,"\n",[245,386,388,391,395,398,401],{"class":247,"line":387},5,[245,389,390],{"class":335},"export",[245,392,394],{"class":393},"spNyl"," function",[245,396,397],{"class":265}," useDb",[245,399,400],{"class":339},"()",[245,402,403],{"class":339}," {\n",[245,405,407,410,413,416,419],{"class":247,"line":406},6,[245,408,409],{"class":393},"  const",[245,411,412],{"class":343}," config",[245,414,415],{"class":339}," =",[245,417,418],{"class":265}," useRuntimeConfig",[245,420,422],{"class":421},"swJcz","()\n",[245,424,426,428,431,433,435,438],{"class":247,"line":425},7,[245,427,409],{"class":393},[245,429,430],{"class":343}," client",[245,432,415],{"class":339},[245,434,344],{"class":265},[245,436,437],{"class":421},"(",[245,439,440],{"class":339},"{\n",[245,442,444,447,449,451,453,455,458,461,464],{"class":247,"line":443},8,[245,445,446],{"class":421},"    url",[245,448,317],{"class":339},[245,450,412],{"class":343},[245,452,178],{"class":339},[245,454,308],{"class":343},[245,456,457],{"class":335}," as",[245,459,460],{"class":251}," string",[245,462,463],{"class":339},",",[245,465,466],{"class":329},"         \u002F\u002F file:.\u002Fdata\u002Fidp.db  OR  libsql:\u002F\u002F...\n",[245,468,470,473,475,477,479,481,483,485,487],{"class":247,"line":469},9,[245,471,472],{"class":421},"    authToken",[245,474,317],{"class":339},[245,476,412],{"class":343},[245,478,178],{"class":339},[245,480,312],{"class":343},[245,482,457],{"class":335},[245,484,460],{"class":251},[245,486,463],{"class":339},[245,488,489],{"class":329}," \u002F\u002F empty for file:\u002F\u002F URLs\n",[245,491,493,496],{"class":247,"line":492},10,[245,494,495],{"class":339},"  }",[245,497,498],{"class":421},")\n",[245,500,502,505,507,509,512,514,516,519,521],{"class":247,"line":501},11,[245,503,504],{"class":335},"  return",[245,506,367],{"class":265},[245,508,437],{"class":421},[245,510,511],{"class":343},"client",[245,513,463],{"class":339},[245,515,340],{"class":339},[245,517,518],{"class":343}," schema",[245,520,347],{"class":339},[245,522,498],{"class":421},[245,524,526],{"class":247,"line":525},12,[245,527,528],{"class":339},"}\n",[530,531,533],"h4",{"id":532},"option-a-local-sqlite-file","Option A — local SQLite file",[236,535,537],{"className":238,"code":536,"language":240,"meta":241,"style":241},"export NUXT_TURSO_URL=\"file:.\u002Fdata\u002Fidp.db\"\nexport NUXT_TURSO_AUTH_TOKEN=\"\"\n",[127,538,539,558],{"__ignoreMap":241},[245,540,541,543,546,549,552,555],{"class":247,"line":248},[245,542,390],{"class":393},[245,544,545],{"class":343}," NUXT_TURSO_URL",[245,547,548],{"class":339},"=",[245,550,551],{"class":339},"\"",[245,553,554],{"class":255},"file:.\u002Fdata\u002Fidp.db",[245,556,557],{"class":339},"\"\n",[245,559,560,562,565,567],{"class":247,"line":262},[245,561,390],{"class":393},[245,563,564],{"class":343}," NUXT_TURSO_AUTH_TOKEN",[245,566,548],{"class":339},[245,568,569],{"class":339},"\"\"\n",[115,571,572,573,576,577,580],{},"The DB file is created on first startup. The startup plugin ",[127,574,575],{},"server\u002Fplugins\u002F02.database.ts"," runs idempotent ",[127,578,579],{},"CREATE TABLE IF NOT EXISTS"," statements for every table the module needs.",[530,582,584],{"id":583},"option-b-turso","Option B — Turso",[236,586,588],{"className":238,"code":587,"language":240,"meta":241,"style":241},"turso db create my-idp\nturso db tokens create my-idp\nexport NUXT_TURSO_URL=\"libsql:\u002F\u002Fmy-idp-YOURORG.turso.io\"\nexport NUXT_TURSO_AUTH_TOKEN=\"eyJhbGciOi…\"\n",[127,589,590,604,617,632],{"__ignoreMap":241},[245,591,592,595,598,601],{"class":247,"line":248},[245,593,594],{"class":251},"turso",[245,596,597],{"class":255}," db",[245,599,600],{"class":255}," create",[245,602,603],{"class":255}," my-idp\n",[245,605,606,608,610,613,615],{"class":247,"line":262},[245,607,594],{"class":251},[245,609,597],{"class":255},[245,611,612],{"class":255}," tokens",[245,614,600],{"class":255},[245,616,603],{"class":255},[245,618,619,621,623,625,627,630],{"class":247,"line":272},[245,620,390],{"class":393},[245,622,545],{"class":343},[245,624,548],{"class":339},[245,626,551],{"class":339},[245,628,629],{"class":255},"libsql:\u002F\u002Fmy-idp-YOURORG.turso.io",[245,631,557],{"class":339},[245,633,634,636,638,640,642,645],{"class":247,"line":281},[245,635,390],{"class":393},[245,637,564],{"class":343},[245,639,548],{"class":339},[245,641,551],{"class":339},[245,643,644],{"class":255},"eyJhbGciOi…",[245,646,557],{"class":339},[231,648,650],{"id":649},"_3-configure-the-idp-module","3. Configure the IdP module",[115,652,653,656],{},[127,654,655],{},"apps\u002Fopenape-free-idp\u002Fnuxt.config.ts"," already includes the module. Set the runtime secrets:",[236,658,660],{"className":238,"code":659,"language":240,"meta":241,"style":241},"export NUXT_OPENAPE_IDP_SESSION_SECRET=$(openssl rand -hex 32)\nexport NUXT_OPENAPE_IDP_MANAGEMENT_TOKEN=$(openssl rand -hex 32)\nexport NUXT_OPENAPE_IDP_ADMIN_EMAILS=\"you@example.com\"\n",[127,661,662,687,706],{"__ignoreMap":241},[245,663,664,666,669,672,675,678,681,685],{"class":247,"line":248},[245,665,390],{"class":393},[245,667,668],{"class":343}," NUXT_OPENAPE_IDP_SESSION_SECRET",[245,670,671],{"class":339},"=$(",[245,673,674],{"class":251},"openssl",[245,676,677],{"class":255}," rand",[245,679,680],{"class":255}," -hex",[245,682,684],{"class":683},"sbssI"," 32",[245,686,498],{"class":339},[245,688,689,691,694,696,698,700,702,704],{"class":247,"line":262},[245,690,390],{"class":393},[245,692,693],{"class":343}," NUXT_OPENAPE_IDP_MANAGEMENT_TOKEN",[245,695,671],{"class":339},[245,697,674],{"class":251},[245,699,677],{"class":255},[245,701,680],{"class":255},[245,703,684],{"class":683},[245,705,498],{"class":339},[245,707,708,710,713,715,717,720],{"class":247,"line":272},[245,709,390],{"class":393},[245,711,712],{"class":343}," NUXT_OPENAPE_IDP_ADMIN_EMAILS",[245,714,548],{"class":339},[245,716,551],{"class":339},[245,718,719],{"class":255},"you@example.com",[245,721,557],{"class":339},[115,723,724],{},"For a single-hostname deployment, also set:",[236,726,728],{"className":238,"code":727,"language":240,"meta":241,"style":241},"export NUXT_OPENAPE_IDP_RP_ID=id.example.com\nexport NUXT_OPENAPE_IDP_RP_ORIGIN=https:\u002F\u002Fid.example.com\nexport NUXT_OPENAPE_IDP_ISSUER=https:\u002F\u002Fid.example.com\n",[127,729,730,742,754],{"__ignoreMap":241},[245,731,732,734,737,739],{"class":247,"line":248},[245,733,390],{"class":393},[245,735,736],{"class":343}," NUXT_OPENAPE_IDP_RP_ID",[245,738,548],{"class":339},[245,740,741],{"class":343},"id.example.com\n",[245,743,744,746,749,751],{"class":247,"line":262},[245,745,390],{"class":393},[245,747,748],{"class":343}," NUXT_OPENAPE_IDP_RP_ORIGIN",[245,750,548],{"class":339},[245,752,753],{"class":343},"https:\u002F\u002Fid.example.com\n",[245,755,756,758,761,763],{"class":247,"line":272},[245,757,390],{"class":393},[245,759,760],{"class":343}," NUXT_OPENAPE_IDP_ISSUER",[245,762,548],{"class":339},[245,764,753],{"class":343},[115,766,767,768,771,772,775],{},"For ",[119,769,770],{"href":73},"multi-tenant",", set ",[127,773,774],{},"rpHostAllowList"," instead and leave the static RP fields empty:",[236,777,779],{"className":238,"code":778,"language":240,"meta":241,"style":241},"export NUXT_OPENAPE_IDP_RP_HOST_ALLOW_LIST=\"id.example.com,id.example.at\"\n",[127,780,781],{"__ignoreMap":241},[245,782,783,785,788,790,792,795],{"class":247,"line":248},[245,784,390],{"class":393},[245,786,787],{"class":343}," NUXT_OPENAPE_IDP_RP_HOST_ALLOW_LIST",[245,789,548],{"class":339},[245,791,551],{"class":339},[245,793,794],{"class":255},"id.example.com,id.example.at",[245,796,557],{"class":339},[231,798,800],{"id":799},"_4-start-it","4. Start it",[236,802,804],{"className":238,"code":803,"language":240,"meta":241,"style":241},"pnpm dev    # http:\u002F\u002Flocalhost:3003\n",[127,805,806],{"__ignoreMap":241},[245,807,808,810,813],{"class":247,"line":248},[245,809,275],{"class":251},[245,811,812],{"class":255}," dev",[245,814,815],{"class":329},"    # http:\u002F\u002Flocalhost:3003\n",[115,817,818,819,822,823,825,826,829],{},"On first request the database tables and indexes are created. The login page is at ",[127,820,821],{},"\u002Flogin","; the admin UI at ",[127,824,184],{}," (accessible to emails in ",[127,827,828],{},"NUXT_OPENAPE_IDP_ADMIN_EMAILS",").",[231,831,833],{"id":832},"_5-create-your-first-user","5. Create your first user",[115,835,836],{},"Use the management token to mint a registration URL:",[236,838,840],{"className":238,"code":839,"language":240,"meta":241,"style":241},"curl -X POST http:\u002F\u002Flocalhost:3003\u002Fapi\u002Fadmin\u002Fregistration-urls \\\n  -H \"Authorization: Bearer $NUXT_OPENAPE_IDP_MANAGEMENT_TOKEN\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"email\":\"you@example.com\",\"name\":\"You\"}'\n",[127,841,842,859,877,890],{"__ignoreMap":241},[245,843,844,847,850,853,856],{"class":247,"line":248},[245,845,846],{"class":251},"curl",[245,848,849],{"class":255}," -X",[245,851,852],{"class":255}," POST",[245,854,855],{"class":255}," http:\u002F\u002Flocalhost:3003\u002Fapi\u002Fadmin\u002Fregistration-urls",[245,857,858],{"class":343}," \\\n",[245,860,861,864,867,870,873,875],{"class":247,"line":262},[245,862,863],{"class":255},"  -H",[245,865,866],{"class":339}," \"",[245,868,869],{"class":255},"Authorization: Bearer ",[245,871,872],{"class":343},"$NUXT_OPENAPE_IDP_MANAGEMENT_TOKEN",[245,874,551],{"class":339},[245,876,858],{"class":343},[245,878,879,881,883,886,888],{"class":247,"line":272},[245,880,863],{"class":255},[245,882,866],{"class":339},[245,884,885],{"class":255},"Content-Type: application\u002Fjson",[245,887,551],{"class":339},[245,889,858],{"class":343},[245,891,892,895,897,900],{"class":247,"line":281},[245,893,894],{"class":255},"  -d",[245,896,353],{"class":339},[245,898,899],{"class":255},"{\"email\":\"you@example.com\",\"name\":\"You\"}",[245,901,358],{"class":339},[115,903,904],{},"Open the returned URL and register a passkey.",[138,906,908],{"id":907},"production-build","Production build",[236,910,912],{"className":238,"code":911,"language":240,"meta":241,"style":241},"pnpm --filter openape-free-idp build\nnode .output\u002Fserver\u002Findex.mjs\n",[127,913,914,927],{"__ignoreMap":241},[245,915,916,918,921,924],{"class":247,"line":248},[245,917,275],{"class":251},[245,919,920],{"class":255}," --filter",[245,922,923],{"class":255}," openape-free-idp",[245,925,926],{"class":255}," build\n",[245,928,929,932],{"class":247,"line":262},[245,930,931],{"class":251},"node",[245,933,934],{"class":255}," .output\u002Fserver\u002Findex.mjs\n",[115,936,937],{},"Put nginx (or another reverse proxy) in front and terminate TLS there. A minimal vhost:",[236,939,943],{"className":940,"code":941,"language":942,"meta":241,"style":241},"language-nginx shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","server {\n  listen 443 ssl http2;\n  server_name id.example.com;\n\n  ssl_certificate     \u002Fetc\u002Fletsencrypt\u002Flive\u002Fid.example.com\u002Ffullchain.pem;\n  ssl_certificate_key \u002Fetc\u002Fletsencrypt\u002Flive\u002Fid.example.com\u002Fprivkey.pem;\n\n  location \u002F {\n    proxy_pass http:\u002F\u002F127.0.0.1:3003;\n    proxy_set_header Host $host;\n    proxy_set_header X-Forwarded-Host $host;\n    proxy_set_header X-Forwarded-Proto $scheme;\n    proxy_http_version 1.1;\n  }\n}\n","nginx",[127,944,945,950,955,960,964,969,974,978,983,988,993,998,1003,1009,1015],{"__ignoreMap":241},[245,946,947],{"class":247,"line":248},[245,948,949],{},"server {\n",[245,951,952],{"class":247,"line":262},[245,953,954],{},"  listen 443 ssl http2;\n",[245,956,957],{"class":247,"line":272},[245,958,959],{},"  server_name id.example.com;\n",[245,961,962],{"class":247,"line":281},[245,963,384],{"emptyLinePlaceholder":383},[245,965,966],{"class":247,"line":387},[245,967,968],{},"  ssl_certificate     \u002Fetc\u002Fletsencrypt\u002Flive\u002Fid.example.com\u002Ffullchain.pem;\n",[245,970,971],{"class":247,"line":406},[245,972,973],{},"  ssl_certificate_key \u002Fetc\u002Fletsencrypt\u002Flive\u002Fid.example.com\u002Fprivkey.pem;\n",[245,975,976],{"class":247,"line":425},[245,977,384],{"emptyLinePlaceholder":383},[245,979,980],{"class":247,"line":443},[245,981,982],{},"  location \u002F {\n",[245,984,985],{"class":247,"line":469},[245,986,987],{},"    proxy_pass http:\u002F\u002F127.0.0.1:3003;\n",[245,989,990],{"class":247,"line":492},[245,991,992],{},"    proxy_set_header Host $host;\n",[245,994,995],{"class":247,"line":501},[245,996,997],{},"    proxy_set_header X-Forwarded-Host $host;\n",[245,999,1000],{"class":247,"line":525},[245,1001,1002],{},"    proxy_set_header X-Forwarded-Proto $scheme;\n",[245,1004,1006],{"class":247,"line":1005},13,[245,1007,1008],{},"    proxy_http_version 1.1;\n",[245,1010,1012],{"class":247,"line":1011},14,[245,1013,1014],{},"  }\n",[245,1016,1018],{"class":247,"line":1017},15,[245,1019,528],{},[115,1021,1022],{},"The runtime is Node 22+ — the same binary works under systemd, a container, or any Node-capable host.",[138,1024,1026],{"id":1025},"migrations","Migrations",[115,1028,1029],{},"The reference app ships two parallel mechanisms:",[143,1031,1032,1042],{},[146,1033,1034,1037,1038,1041],{},[216,1035,1036],{},"Drizzle-kit migrations"," under ",[127,1039,1040],{},"server\u002Fdatabase\u002Fmigrations\u002F"," — used in dev to track schema history and generate DDL.",[146,1043,1044,1047,1048,1050,1051,1053,1054,1057,1058,1061],{},[216,1045,1046],{},"Idempotent startup init"," in ",[127,1049,575],{}," — the authoritative path in production. ",[127,1052,579],{}," + ",[127,1055,1056],{},"ALTER TABLE ADD COLUMN"," guarded by ",[127,1059,1060],{},"PRAGMA table_info"," lookups. Safe to run on every boot; handles new deploys and schema evolution without a separate migration step.",[115,1063,1064,1065,1068],{},"When you add a schema change, update both: write the Drizzle migration for version control, and mirror the DDL in ",[127,1066,1067],{},"02.database.ts"," so fresh nodes come up cleanly.",[138,1070,1072],{"id":1071},"customizing","Customizing",[115,1074,1075,1076,317],{},"Every store can be swapped by registering a different factory in ",[127,1077,1078],{},"server\u002Fplugins\u002F04.idp-stores.ts",[236,1080,1082],{"className":320,"code":1081,"language":322,"meta":241,"style":241},"import { defineUserStore, defineCredentialStore, … } from '#imports'\n\nexport default defineNitroPlugin(() => {\n  defineUserStore(() => myPostgresUserStore)\n  defineCredentialStore(() => myPostgresCredentialStore)\n  \u002F\u002F …\n})\n",[127,1083,1084,1115,1119,1138,1154,1170,1175],{"__ignoreMap":241},[245,1085,1086,1088,1090,1093,1095,1098,1100,1103,1106,1108,1110,1113],{"class":247,"line":248},[245,1087,336],{"class":335},[245,1089,340],{"class":339},[245,1091,1092],{"class":343}," defineUserStore",[245,1094,463],{"class":339},[245,1096,1097],{"class":343}," defineCredentialStore",[245,1099,463],{"class":339},[245,1101,1102],{"class":421}," … ",[245,1104,1105],{"class":339},"}",[245,1107,350],{"class":335},[245,1109,353],{"class":339},[245,1111,1112],{"class":255},"#imports",[245,1114,358],{"class":339},[245,1116,1117],{"class":247,"line":262},[245,1118,384],{"emptyLinePlaceholder":383},[245,1120,1121,1123,1126,1129,1131,1133,1136],{"class":247,"line":272},[245,1122,390],{"class":335},[245,1124,1125],{"class":335}," default",[245,1127,1128],{"class":265}," defineNitroPlugin",[245,1130,437],{"class":343},[245,1132,400],{"class":339},[245,1134,1135],{"class":393}," =>",[245,1137,403],{"class":339},[245,1139,1140,1143,1145,1147,1149,1152],{"class":247,"line":281},[245,1141,1142],{"class":265},"  defineUserStore",[245,1144,437],{"class":421},[245,1146,400],{"class":339},[245,1148,1135],{"class":393},[245,1150,1151],{"class":343}," myPostgresUserStore",[245,1153,498],{"class":421},[245,1155,1156,1159,1161,1163,1165,1168],{"class":247,"line":387},[245,1157,1158],{"class":265},"  defineCredentialStore",[245,1160,437],{"class":421},[245,1162,400],{"class":339},[245,1164,1135],{"class":393},[245,1166,1167],{"class":343}," myPostgresCredentialStore",[245,1169,498],{"class":421},[245,1171,1172],{"class":247,"line":406},[245,1173,1174],{"class":329},"  \u002F\u002F …\n",[245,1176,1177,1179],{"class":247,"line":425},[245,1178,1105],{"class":339},[245,1180,498],{"class":343},[115,1182,1183],{},"Common reasons to replace stores:",[143,1185,1186,1189,1192],{},[146,1187,1188],{},"Swap libsql for Postgres \u002F DynamoDB \u002F MongoDB.",[146,1190,1191],{},"Add caching in front of the credential lookup.",[146,1193,1194],{},"Plug into an existing identity provider during migration.",[115,1196,1197,1198,155,1201,155,1204,1207,1208,1211],{},"The ",[127,1199,1200],{},"CredentialStore",[127,1202,1203],{},"UserStore",[127,1205,1206],{},"CodeStore",", etc. interfaces are exported from ",[127,1209,1210],{},"@openape\u002Fauth","; implementing each is a few methods per store.",[138,1213,1215],{"id":1214},"related","Related",[143,1217,1218,1223,1228],{},[146,1219,1220,1222],{},[119,1221,68],{"href":69}," — Module options and route reference",[146,1224,1225,1227],{},[119,1226,72],{"href":73}," — Host multiple domains from one instance",[146,1229,1230,1233],{},[119,1231,1232],{"href":53},"Auth"," — Authentication modes and flows",[1235,1236,1237],"style",{},"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 .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}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 .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 .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}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 .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":241,"searchDepth":248,"depth":262,"links":1239},[1240,1241,1242,1249,1250,1251,1252],{"id":140,"depth":262,"text":141},{"id":198,"depth":262,"text":199},{"id":228,"depth":262,"text":229,"children":1243},[1244,1245,1246,1247,1248],{"id":233,"depth":272,"text":234},{"id":289,"depth":272,"text":290},{"id":649,"depth":272,"text":650},{"id":799,"depth":272,"text":800},{"id":832,"depth":272,"text":833},{"id":907,"depth":262,"text":908},{"id":1025,"depth":262,"text":1026},{"id":1071,"depth":262,"text":1072},{"id":1214,"depth":262,"text":1215},"Run your own persistent OpenApe IdP with Drizzle + libsql.","md",null,{},{"title":40,"description":1253},"EVizZ7-BG_Y4kjtk450mUFiDmIZ5djsCdrY73JsevyQ",[1260,1262],{"title":36,"path":37,"stem":38,"description":1261,"children":-1},"Install and use the apes command-line interface and the ape-shell wrapper.",{"title":50,"path":46,"stem":47,"description":1263,"children":-1},"The OpenApe package ecosystem.",1781287647231]