[{"data":1,"prerenderedAt":847},["ShallowReactive",2],{"-docs-deploy-onprem":3,"-docs-deploy-onprem-surround":836},{"id":4,"title":5,"body":6,"description":826,"extension":827,"links":828,"meta":829,"navigation":830,"path":832,"seo":833,"stem":834,"__hash__":835},"docs\u002Fdocs\u002F3.deploy\u002F2.onprem.md","On-prem deploy",{"type":7,"value":8,"toc":815},"minimark",[9,18,23,93,96,126,140,144,151,192,203,207,226,273,282,286,289,402,420,424,431,586,597,601,608,672,683,687,694,715,722,762,766,778,789,793,811],[10,11,12,13,17],"p",{},"On-prem, you bring your own Postgres, object storage, and model credentials — ",[14,15,16],"strong",{},"we never store your data",". The server image migrates on boot and serves the API, auth, and web SPA from one origin.",[19,20,22],"h2",{"id":21},"quickstart-docker-compose","Quickstart (Docker Compose)",[24,25,31],"pre",{"className":26,"code":27,"filename":28,"language":29,"meta":30,"style":30},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","cp deploy\u002F.env.example deploy\u002F.env\n# Edit deploy\u002F.env — at minimum set:\n#   BETTER_AUTH_SECRET   (openssl rand -base64 48)\n#   LICENSE_JWT          (from your vendor; without it the instance is read-only)\ndocker compose -f deploy\u002Fdocker-compose.yml up -d --build\n","deploy\u002F","bash","",[32,33,34,50,57,63,69],"code",{"__ignoreMap":30},[35,36,39,43,47],"span",{"class":37,"line":38},"line",1,[35,40,42],{"class":41},"sBMFI","cp",[35,44,46],{"class":45},"sfazB"," deploy\u002F.env.example",[35,48,49],{"class":45}," deploy\u002F.env\n",[35,51,53],{"class":37,"line":52},2,[35,54,56],{"class":55},"sHwdD","# Edit deploy\u002F.env — at minimum set:\n",[35,58,60],{"class":37,"line":59},3,[35,61,62],{"class":55},"#   BETTER_AUTH_SECRET   (openssl rand -base64 48)\n",[35,64,66],{"class":37,"line":65},4,[35,67,68],{"class":55},"#   LICENSE_JWT          (from your vendor; without it the instance is read-only)\n",[35,70,72,75,78,81,84,87,90],{"class":37,"line":71},5,[35,73,74],{"class":41},"docker",[35,76,77],{"class":45}," compose",[35,79,80],{"class":45}," -f",[35,82,83],{"class":45}," deploy\u002Fdocker-compose.yml",[35,85,86],{"class":45}," up",[35,88,89],{"class":45}," -d",[35,91,92],{"class":45}," --build\n",[10,94,95],{},"Then verify:",[24,97,99],{"className":26,"code":98,"language":29,"meta":30,"style":30},"curl -fsS http:\u002F\u002Flocalhost:8080\u002Fhealthz     # {\"status\":\"ok\",\"mode\":\"onprem\",...}\nopen http:\u002F\u002Flocalhost:8080\u002F                  # the web UI (same origin as the API)\n",[32,100,101,115],{"__ignoreMap":30},[35,102,103,106,109,112],{"class":37,"line":38},[35,104,105],{"class":41},"curl",[35,107,108],{"class":45}," -fsS",[35,110,111],{"class":45}," http:\u002F\u002Flocalhost:8080\u002Fhealthz",[35,113,114],{"class":55},"     # {\"status\":\"ok\",\"mode\":\"onprem\",...}\n",[35,116,117,120,123],{"class":37,"line":52},[35,118,119],{"class":41},"open",[35,121,122],{"class":45}," http:\u002F\u002Flocalhost:8080\u002F",[35,124,125],{"class":55},"                  # the web UI (same origin as the API)\n",[10,127,128,131,132,135,136,139],{},[32,129,130],{},"up"," builds the image (context = repo root), starts Postgres 16 + the server, and the server ",[14,133,134],{},"migrates-on-boot"," then serves. Add ",[32,137,138],{},"--profile s3"," to also start a bundled MinIO.",[19,141,143],{"id":142},"bring-your-own-postgres","Bring your own Postgres",[10,145,146,147,150],{},"Point ",[32,148,149],{},"DATABASE_URL"," at your own database and drop the bundled service:",[24,152,154],{"className":26,"code":153,"language":29,"meta":30,"style":30},"DATABASE_URL=postgres:\u002F\u002Fuser:pass@db.internal:5432\u002Fthespider \\\n  docker compose -f deploy\u002Fdocker-compose.yml up -d server   # start only the server\n",[32,155,156,171],{"__ignoreMap":30},[35,157,158,161,165,168],{"class":37,"line":38},[35,159,149],{"class":160},"sTEyZ",[35,162,164],{"class":163},"sMK4o","=",[35,166,167],{"class":45},"postgres:\u002F\u002Fuser:pass@db.internal:5432\u002Fthespider",[35,169,170],{"class":41}," \\\n",[35,172,173,176,178,180,182,184,186,189],{"class":37,"line":52},[35,174,175],{"class":45},"  docker",[35,177,77],{"class":45},[35,179,80],{"class":45},[35,181,83],{"class":45},[35,183,86],{"class":45},[35,185,89],{"class":45},[35,187,188],{"class":45}," server",[35,190,191],{"class":55},"   # start only the server\n",[10,193,194,195,198,199,202],{},"The server needs a role that can create tables and (for RLS) ",[32,196,197],{},"SET ROLE app_role"," — a normal owner role of the target database is sufficient; migrate-on-boot creates ",[32,200,201],{},"app_role"," itself.",[19,204,206],{"id":205},"bring-your-own-object-storage","Bring your own object storage",[10,208,209,210,213,214,217,218,221,222,225],{},"Slice artifacts (rendered prompt \u002F raw stdout \u002F stderr) are ",[14,211,212],{},"content-addressed by sha256"," provenance blobs — the parsed content already lives in ",[32,215,216],{},"reports",". Default is the on-disk ",[32,219,220],{},"ARTIFACTS_DIR"," volume. For S3\u002FMinIO\u002FR2, set ",[32,223,224],{},"S3_BUCKET"," (+ endpoint\u002Fcreds):",[24,227,229],{"className":26,"code":228,"filename":28,"language":29,"meta":30,"style":30},"# bundled MinIO:\ndocker compose -f deploy\u002Fdocker-compose.yml --profile s3 up -d --build\n# then in .env: S3_BUCKET=thespider  S3_ENDPOINT=http:\u002F\u002Fminio:9000\n#               S3_ACCESS_KEY_ID=thespider  S3_SECRET_ACCESS_KEY=thespider123\n# create the bucket once (mc mb) — the server does not auto-create it.\n",[32,230,231,236,258,263,268],{"__ignoreMap":30},[35,232,233],{"class":37,"line":38},[35,234,235],{"class":55},"# bundled MinIO:\n",[35,237,238,240,242,244,246,249,252,254,256],{"class":37,"line":52},[35,239,74],{"class":41},[35,241,77],{"class":45},[35,243,80],{"class":45},[35,245,83],{"class":45},[35,247,248],{"class":45}," --profile",[35,250,251],{"class":45}," s3",[35,253,86],{"class":45},[35,255,89],{"class":45},[35,257,92],{"class":45},[35,259,260],{"class":37,"line":59},[35,261,262],{"class":55},"# then in .env: S3_BUCKET=thespider  S3_ENDPOINT=http:\u002F\u002Fminio:9000\n",[35,264,265],{"class":37,"line":65},[35,266,267],{"class":55},"#               S3_ACCESS_KEY_ID=thespider  S3_SECRET_ACCESS_KEY=thespider123\n",[35,269,270],{"class":37,"line":71},[35,271,272],{"class":55},"# create the bucket once (mc mb) — the server does not auto-create it.\n",[10,274,275,276,281],{},"See ",[277,278,280],"a",{"href":279},"\u002Fdocs\u002Fguide\u002Fdata-handling#set-retention","Data handling"," for the 90-day artifact lifecycle recipe.",[19,283,285],{"id":284},"enroll-a-client","Enroll a client",[10,287,288],{},"Mint a single-use enrollment code inside the container, then drive the Rust agent:",[24,290,292],{"className":26,"code":291,"language":29,"meta":30,"style":30},"docker compose -f deploy\u002Fdocker-compose.yml exec server \\\n  bun run src\u002Fmint-enrollment-code.ts --org default --project default\n# copy the printed `code:` value, then on a machine with model CLIs + a checkout:\necho \"\u003Ccode>\" | thespider-agent login          # server = http:\u002F\u002F\u003Chost>:8080 in config.toml\nthespider-agent run                             # claim → run models locally → submit findings\nthespider-agent gate --campaign \u003Cid>            # CI-style pass\u002Ffail\n",[32,293,294,311,334,339,366,376],{"__ignoreMap":30},[35,295,296,298,300,302,304,307,309],{"class":37,"line":38},[35,297,74],{"class":41},[35,299,77],{"class":45},[35,301,80],{"class":45},[35,303,83],{"class":45},[35,305,306],{"class":45}," exec",[35,308,188],{"class":45},[35,310,170],{"class":160},[35,312,313,316,319,322,325,328,331],{"class":37,"line":52},[35,314,315],{"class":45},"  bun",[35,317,318],{"class":45}," run",[35,320,321],{"class":45}," src\u002Fmint-enrollment-code.ts",[35,323,324],{"class":45}," --org",[35,326,327],{"class":45}," default",[35,329,330],{"class":45}," --project",[35,332,333],{"class":45}," default\n",[35,335,336],{"class":37,"line":59},[35,337,338],{"class":55},"# copy the printed `code:` value, then on a machine with model CLIs + a checkout:\n",[35,340,341,345,348,351,354,357,360,363],{"class":37,"line":65},[35,342,344],{"class":343},"s2Zo4","echo",[35,346,347],{"class":163}," \"",[35,349,350],{"class":45},"\u003Ccode>",[35,352,353],{"class":163},"\"",[35,355,356],{"class":163}," |",[35,358,359],{"class":41}," thespider-agent",[35,361,362],{"class":45}," login",[35,364,365],{"class":55},"          # server = http:\u002F\u002F\u003Chost>:8080 in config.toml\n",[35,367,368,371,373],{"class":37,"line":71},[35,369,370],{"class":41},"thespider-agent",[35,372,318],{"class":45},[35,374,375],{"class":55},"                             # claim → run models locally → submit findings\n",[35,377,379,381,384,387,390,393,396,399],{"class":37,"line":378},6,[35,380,370],{"class":41},[35,382,383],{"class":45}," gate",[35,385,386],{"class":45}," --campaign",[35,388,389],{"class":163}," \u003C",[35,391,392],{"class":45},"i",[35,394,395],{"class":160},"d",[35,397,398],{"class":163},">",[35,400,401],{"class":55},"            # CI-style pass\u002Ffail\n",[10,403,404,405,408,409,412,413,416,417,419],{},"The default project is ",[14,406,407],{},"campaign-ready at first boot"," — the image bundles a default methodology snapshot seeded as ",[32,410,411],{},"config_version"," 1, with the ",[32,414,415],{},"sonnet"," model enabled. Author your real buckets\u002Fpasses\u002Fcategories in the web UI (each save becomes a new immutable ",[32,418,411],{},").",[19,421,423],{"id":422},"deploy-on-kubernetes-helm","Deploy on Kubernetes (Helm)",[10,425,426,427,430],{},"A minimal chart lives at ",[32,428,429],{},"deploy\u002Fhelm\u002Fthespider"," (BYO Postgres — the chart deploys none):",[24,432,434],{"className":26,"code":433,"language":29,"meta":30,"style":30},"helm install ts deploy\u002Fhelm\u002Fthespider \\\n  --set secrets.betterAuthSecret=\"$(openssl rand -base64 48)\" \\\n  --set secrets.licenseJwt=\"\u003Cjwt>\" \\\n  --set config.databaseUrl=\"postgres:\u002F\u002Fuser:pass@postgres:5432\u002Fthespider\" \\\n  --set image.repository=\u003Cyour-registry>\u002Fthespider-server --set image.tag=\u003Cversion>\n# expose it:\nhelm upgrade ts deploy\u002Fhelm\u002Fthespider --reuse-values \\\n  --set ingress.enabled=true --set ingress.host=thespider.example.com\n",[32,435,436,452,478,494,510,548,553,570],{"__ignoreMap":30},[35,437,438,441,444,447,450],{"class":37,"line":38},[35,439,440],{"class":41},"helm",[35,442,443],{"class":45}," install",[35,445,446],{"class":45}," ts",[35,448,449],{"class":45}," deploy\u002Fhelm\u002Fthespider",[35,451,170],{"class":160},[35,453,454,457,460,463,466,469,473,476],{"class":37,"line":52},[35,455,456],{"class":45},"  --set",[35,458,459],{"class":45}," secrets.betterAuthSecret=",[35,461,462],{"class":163},"\"$(",[35,464,465],{"class":41},"openssl",[35,467,468],{"class":45}," rand -base64 ",[35,470,472],{"class":471},"sbssI","48",[35,474,475],{"class":163},")\"",[35,477,170],{"class":160},[35,479,480,482,485,487,490,492],{"class":37,"line":59},[35,481,456],{"class":45},[35,483,484],{"class":45}," secrets.licenseJwt=",[35,486,353],{"class":163},[35,488,489],{"class":45},"\u003Cjwt>",[35,491,353],{"class":163},[35,493,170],{"class":160},[35,495,496,498,501,503,506,508],{"class":37,"line":65},[35,497,456],{"class":45},[35,499,500],{"class":45}," config.databaseUrl=",[35,502,353],{"class":163},[35,504,505],{"class":45},"postgres:\u002F\u002Fuser:pass@postgres:5432\u002Fthespider",[35,507,353],{"class":163},[35,509,170],{"class":160},[35,511,512,514,517,520,523,526,528,531,534,537,539,542,545],{"class":37,"line":71},[35,513,456],{"class":45},[35,515,516],{"class":45}," image.repository=",[35,518,519],{"class":163},"\u003C",[35,521,522],{"class":45},"your-registr",[35,524,525],{"class":160},"y",[35,527,398],{"class":163},[35,529,530],{"class":45},"\u002Fthespider-server",[35,532,533],{"class":45}," --set",[35,535,536],{"class":45}," image.tag=",[35,538,519],{"class":163},[35,540,541],{"class":45},"versio",[35,543,544],{"class":160},"n",[35,546,547],{"class":163},">\n",[35,549,550],{"class":37,"line":378},[35,551,552],{"class":55},"# expose it:\n",[35,554,556,558,561,563,565,568],{"class":37,"line":555},7,[35,557,440],{"class":41},[35,559,560],{"class":45}," upgrade",[35,562,446],{"class":45},[35,564,449],{"class":45},[35,566,567],{"class":45}," --reuse-values",[35,569,170],{"class":160},[35,571,573,575,578,581,583],{"class":37,"line":572},8,[35,574,456],{"class":45},[35,576,577],{"class":45}," ingress.enabled=",[35,579,580],{"class":163},"true",[35,582,533],{"class":45},[35,584,585],{"class":45}," ingress.host=thespider.example.com\n",[10,587,588,589,592,593,596],{},"The chart renders a Deployment, Service, optional Ingress, a Secret (license\u002Fauth\u002FS3\u002FAI keys), and a PVC for the on-disk artifact store. ",[32,590,591],{},"helm lint"," + ",[32,594,595],{},"helm template"," validate it.",[19,598,600],{"id":599},"back-up","Back up",[10,602,603,604,607],{},"The ",[14,605,606],{},"Postgres database is the source of truth"," — it holds every finding, triage decision, config version, and usage event. Back it up on a schedule:",[24,609,611],{"className":26,"code":610,"language":29,"meta":30,"style":30},"docker compose -f deploy\u002Fdocker-compose.yml exec -T postgres \\\n  pg_dump -U postgres thespider | gzip > thespider-$(date +%F).sql.gz\n",[32,612,613,633],{"__ignoreMap":30},[35,614,615,617,619,621,623,625,628,631],{"class":37,"line":38},[35,616,74],{"class":41},[35,618,77],{"class":45},[35,620,80],{"class":45},[35,622,83],{"class":45},[35,624,306],{"class":45},[35,626,627],{"class":45}," -T",[35,629,630],{"class":45}," postgres",[35,632,170],{"class":160},[35,634,635,638,641,643,646,648,651,654,657,660,663,666,669],{"class":37,"line":52},[35,636,637],{"class":45},"  pg_dump",[35,639,640],{"class":45}," -U",[35,642,630],{"class":45},[35,644,645],{"class":45}," thespider",[35,647,356],{"class":163},[35,649,650],{"class":41}," gzip",[35,652,653],{"class":163}," >",[35,655,656],{"class":45}," thespider-",[35,658,659],{"class":163},"$(",[35,661,662],{"class":41},"date",[35,664,665],{"class":45}," +%F",[35,667,668],{"class":163},")",[35,670,671],{"class":45},".sql.gz\n",[10,673,674,675,678,679,682],{},"Artifacts (the ",[32,676,677],{},"thespider_artifacts"," volume \u002F S3 bucket) are regenerable provenance — back them up only if you need the raw prompt\u002Foutput blobs. The ",[32,680,681],{},"thespider_pg"," volume is the one that matters.",[19,684,686],{"id":685},"upgrade","Upgrade",[10,688,689,690,693],{},"Pull\u002Frebuild the new image and restart; ",[14,691,692],{},"migrate-on-boot"," applies additive migrations automatically:",[24,695,697],{"className":26,"code":696,"language":29,"meta":30,"style":30},"docker compose -f deploy\u002Fdocker-compose.yml up -d --build\n",[32,698,699],{"__ignoreMap":30},[35,700,701,703,705,707,709,711,713],{"class":37,"line":38},[35,702,74],{"class":41},[35,704,77],{"class":45},[35,706,80],{"class":45},[35,708,83],{"class":45},[35,710,86],{"class":45},[35,712,89],{"class":45},[35,714,92],{"class":45},[10,716,717,718,721],{},"To refresh methodology, pull an update bundle from the vendor (",[32,719,720],{},"GET \u002Fv1\u002Fmethod-updates",", license-JWT-authenticated) and apply it as a new immutable config version:",[24,723,725],{"className":26,"code":724,"language":29,"meta":30,"style":30},"docker compose -f deploy\u002Fdocker-compose.yml exec server \\\n  bun run src\u002Fapply-methodology.ts --project default --file \u002Fpath\u002Fto\u002Fsnapshot.json\n",[32,726,727,743],{"__ignoreMap":30},[35,728,729,731,733,735,737,739,741],{"class":37,"line":38},[35,730,74],{"class":41},[35,732,77],{"class":45},[35,734,80],{"class":45},[35,736,83],{"class":45},[35,738,306],{"class":45},[35,740,188],{"class":45},[35,742,170],{"class":160},[35,744,745,747,749,752,754,756,759],{"class":37,"line":52},[35,746,315],{"class":45},[35,748,318],{"class":45},[35,750,751],{"class":45}," src\u002Fapply-methodology.ts",[35,753,330],{"class":45},[35,755,327],{"class":45},[35,757,758],{"class":45}," --file",[35,760,761],{"class":45}," \u002Fpath\u002Fto\u002Fsnapshot.json\n",[19,763,765],{"id":764},"verify-the-install","Verify the install",[24,767,769],{"className":26,"code":768,"language":29,"meta":30,"style":30},"bash deploy\u002Fverify-onprem.sh\n",[32,770,771],{"__ignoreMap":30},[35,772,773,775],{"class":37,"line":38},[35,774,29],{"class":41},[35,776,777],{"class":45}," deploy\u002Fverify-onprem.sh\n",[10,779,275,780,788],{},[277,781,785],{"href":782,"rel":783},"https:\u002F\u002Fgitlab.com\u002Fdragonstone-labs\u002Fthespider-saas\u002F-\u002Fblob\u002Fmain\u002Fdeploy\u002Fair-gap.md",[784],"nofollow",[32,786,787],{},"deploy\u002Fair-gap.md"," for the fully-offline story.",[19,790,792],{"id":791},"next-steps","Next steps",[794,795,796,804],"ul",{},[797,798,799,803],"li",{},[277,800,802],{"href":801},"\u002Fdocs\u002Fdeploy\u002Fdeployment-modes","Deployment modes"," — licensing & entitlements.",[797,805,806,810],{},[277,807,809],{"href":808},"\u002Fdocs\u002Freference\u002Fenv-vars","Environment reference"," — every variable.",[812,813,814],"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 .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 .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 .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":30,"searchDepth":52,"depth":52,"links":816},[817,818,819,820,821,822,823,824,825],{"id":21,"depth":52,"text":22},{"id":142,"depth":52,"text":143},{"id":205,"depth":52,"text":206},{"id":284,"depth":52,"text":285},{"id":422,"depth":52,"text":423},{"id":599,"depth":52,"text":600},{"id":685,"depth":52,"text":686},{"id":764,"depth":52,"text":765},{"id":791,"depth":52,"text":792},"Install TheSpider on-prem with Docker Compose or Helm","md",null,{},{"icon":831},"i-lucide-hard-drive-download","\u002Fdocs\u002Fdeploy\u002Fonprem",{"title":5,"description":826},"docs\u002F3.deploy\u002F2.onprem","sh06IyyiUKoTUzX-sf_R_AOWSCeWCTQBN1qmuRTe8ME",[837,841],{"title":802,"path":801,"stem":838,"description":839,"icon":840,"children":-1},"docs\u002F3.deploy\u002F1.deployment-modes","One Docker image runs as SaaS or on-prem — pick your mode","i-lucide-boxes",{"title":842,"path":843,"stem":844,"description":845,"icon":846,"children":-1},"Air-gap deploy","\u002Fdocs\u002Fdeploy\u002Fairgap","docs\u002F3.deploy\u002F3.airgap","Run TheSpider fully offline with no phone-home","i-lucide-wifi-off",1783955418907]