[{"data":1,"prerenderedAt":11982},["ShallowReactive",2],{"docs-\u002Fdocs\u002Farchitecture\u002Fsecurity":3,"docs-sidebar":814},{"id":4,"title":5,"body":6,"description":805,"extension":806,"icon":807,"meta":808,"navigation":403,"path":809,"section":810,"seo":811,"stem":812,"__hash__":813},"docs\u002Fdocs\u002F4.architecture\u002F4.security.md","Security",{"type":7,"value":8,"toc":791},"minimark",[9,13,17,22,130,133,144,163,168,283,328,332,342,350,353,364,368,371,427,434,438,549,553,556,584,588,591,675,682,686,689,707,711,760,764,787],[10,11,5],"h1",{"id":12},"security",[14,15,16],"p",{},"Reqcore enforces security at multiple layers: authentication, tenant isolation, input validation, document access, and HTTP headers.",[18,19,21],"h2",{"id":20},"security-boundaries","Security Boundaries",[23,24,25,38],"table",{},[26,27,28],"thead",{},[29,30,31,35],"tr",{},[32,33,34],"th",{},"Boundary",[32,36,37],{},"Enforcement",[39,40,41,57,67,80,97,110,120],"tbody",{},[29,42,43,50],{},[44,45,46],"td",{},[47,48,49],"strong",{},"Authentication",[44,51,52,56],{},[53,54,55],"code",{},"requireAuth(event)"," — throws 401 if no session",[29,58,59,64],{},[44,60,61],{},[47,62,63],{},"Organization membership",[44,65,66],{},"Better Auth org plugin — users can only access orgs they belong to",[29,68,69,74],{},[44,70,71],{},[47,72,73],{},"Tenant data isolation",[44,75,76,77],{},"Every query includes ",[53,78,79],{},"eq(table.organizationId, orgId)",[29,81,82,87],{},[44,83,84],{},[47,85,86],{},"Input validation",[44,88,89,90,93,94],{},"Zod schemas via ",[53,91,92],{},"readValidatedBody"," \u002F ",[53,95,96],{},"getValidatedQuery",[29,98,99,104],{},[44,100,101],{},[47,102,103],{},"Rate limiting",[44,105,106,109],{},[53,107,108],{},"createRateLimiter()"," on public endpoints (sliding window by IP)",[29,111,112,117],{},[44,113,114],{},[47,115,116],{},"Document access",[44,118,119],{},"Server-proxied streaming — no presigned URLs",[29,121,122,127],{},[44,123,124],{},[47,125,126],{},"Security headers",[44,128,129],{},"Global Nitro route rules",[18,131,49],{"id":132},"authentication",[14,134,135,136,143],{},"Reqcore uses ",[137,138,142],"a",{"href":139,"rel":140},"https:\u002F\u002Fwww.better-auth.com\u002F",[141],"nofollow","Better Auth"," for session-based authentication:",[145,146,147,151,157],"ul",{},[148,149,150],"li",{},"Sessions are stored server-side and identified by an HTTP-only cookie",[148,152,153,154],{},"Auth routes are handled by a catch-all at ",[53,155,156],{},"server\u002Fapi\u002Fauth\u002F[...all].ts",[148,158,159,160],{},"Client-side auth is managed via ",[53,161,162],{},"app\u002Futils\u002Fauth-client.ts",[164,165,167],"h3",{"id":166},"route-protection","Route Protection",[169,170,175],"pre",{"className":171,"code":172,"language":173,"meta":174,"style":174},"language-ts shiki shiki-themes github-light github-dark","\u002F\u002F Server: Protect an API route\nexport default defineEventHandler(async (event) => {\n  const { session, user } = await requireAuth(event)\n  const orgId = session.activeOrganizationId\n  \u002F\u002F All queries scoped to orgId\n})\n","ts","",[53,176,177,186,223,257,271,277],{"__ignoreMap":174},[178,179,182],"span",{"class":180,"line":181},"line",1,[178,183,185],{"class":184},"sJ8bj","\u002F\u002F Server: Protect an API route\n",[178,187,189,193,196,200,204,207,210,214,217,220],{"class":180,"line":188},2,[178,190,192],{"class":191},"szBVR","export",[178,194,195],{"class":191}," default",[178,197,199],{"class":198},"sScJk"," defineEventHandler",[178,201,203],{"class":202},"sVt8B","(",[178,205,206],{"class":191},"async",[178,208,209],{"class":202}," (",[178,211,213],{"class":212},"s4XuR","event",[178,215,216],{"class":202},") ",[178,218,219],{"class":191},"=>",[178,221,222],{"class":202}," {\n",[178,224,226,229,232,236,239,242,245,248,251,254],{"class":180,"line":225},3,[178,227,228],{"class":191},"  const",[178,230,231],{"class":202}," { ",[178,233,235],{"class":234},"sj4cs","session",[178,237,238],{"class":202},", ",[178,240,241],{"class":234},"user",[178,243,244],{"class":202}," } ",[178,246,247],{"class":191},"=",[178,249,250],{"class":191}," await",[178,252,253],{"class":198}," requireAuth",[178,255,256],{"class":202},"(event)\n",[178,258,260,262,265,268],{"class":180,"line":259},4,[178,261,228],{"class":191},[178,263,264],{"class":234}," orgId",[178,266,267],{"class":191}," =",[178,269,270],{"class":202}," session.activeOrganizationId\n",[178,272,274],{"class":180,"line":273},5,[178,275,276],{"class":184},"  \u002F\u002F All queries scoped to orgId\n",[178,278,280],{"class":180,"line":279},6,[178,281,282],{"class":202},"})\n",[169,284,286],{"className":171,"code":285,"language":173,"meta":174,"style":174},"\u002F\u002F Client: Redirect unauthenticated users\n\u002F\u002F app\u002Fmiddleware\u002Fauth.ts\nexport default defineNuxtRouteMiddleware((to) => {\n  \u002F\u002F Redirects to \u002Fauth\u002Fsign-in if not authenticated\n})\n",[53,287,288,293,298,319,324],{"__ignoreMap":174},[178,289,290],{"class":180,"line":181},[178,291,292],{"class":184},"\u002F\u002F Client: Redirect unauthenticated users\n",[178,294,295],{"class":180,"line":188},[178,296,297],{"class":184},"\u002F\u002F app\u002Fmiddleware\u002Fauth.ts\n",[178,299,300,302,304,307,310,313,315,317],{"class":180,"line":225},[178,301,192],{"class":191},[178,303,195],{"class":191},[178,305,306],{"class":198}," defineNuxtRouteMiddleware",[178,308,309],{"class":202},"((",[178,311,312],{"class":212},"to",[178,314,216],{"class":202},[178,316,219],{"class":191},[178,318,222],{"class":202},[178,320,321],{"class":180,"line":259},[178,322,323],{"class":184},"  \u002F\u002F Redirects to \u002Fauth\u002Fsign-in if not authenticated\n",[178,325,326],{"class":180,"line":273},[178,327,282],{"class":202},[18,329,331],{"id":330},"multi-tenant-isolation","Multi-Tenant Isolation",[14,333,334,337,338,341],{},[47,335,336],{},"The #1 security invariant",": The organization ID is ",[47,339,340],{},"always"," derived from the authenticated session — never from user input (request body, query parameters, or URL params).",[169,343,348],{"className":344,"code":346,"language":347},[345],"language-text","Request → Auth Guard → Extract orgId from session → Scope all queries by orgId\n","text",[53,349,346],{"__ignoreMap":174},[14,351,352],{},"This prevents:",[145,354,355,358,361],{},[148,356,357],{},"Cross-tenant data access",[148,359,360],{},"Escalation attacks via URL manipulation",[148,362,363],{},"Data leakage between organizations",[18,365,367],{"id":366},"input-validation","Input Validation",[14,369,370],{},"All API endpoints validate input using Zod schemas:",[169,372,374],{"className":171,"code":373,"language":173,"meta":174,"style":174},"\u002F\u002F Server: Validate request body\nconst body = await readValidatedBody(event, createJobSchema.parse)\n\n\u002F\u002F Server: Validate query parameters\nconst query = await getValidatedQuery(event, listJobsSchema.parse)\n",[53,375,376,381,399,405,410],{"__ignoreMap":174},[178,377,378],{"class":180,"line":181},[178,379,380],{"class":184},"\u002F\u002F Server: Validate request body\n",[178,382,383,386,389,391,393,396],{"class":180,"line":188},[178,384,385],{"class":191},"const",[178,387,388],{"class":234}," body",[178,390,267],{"class":191},[178,392,250],{"class":191},[178,394,395],{"class":198}," readValidatedBody",[178,397,398],{"class":202},"(event, createJobSchema.parse)\n",[178,400,401],{"class":180,"line":225},[178,402,404],{"emptyLinePlaceholder":403},true,"\n",[178,406,407],{"class":180,"line":259},[178,408,409],{"class":184},"\u002F\u002F Server: Validate query parameters\n",[178,411,412,414,417,419,421,424],{"class":180,"line":273},[178,413,385],{"class":191},[178,415,416],{"class":234}," query",[178,418,267],{"class":191},[178,420,250],{"class":191},[178,422,423],{"class":198}," getValidatedQuery",[178,425,426],{"class":202},"(event, listJobsSchema.parse)\n",[14,428,429,430,433],{},"Validation schemas are defined in ",[53,431,432],{},"server\u002Futils\u002Fschemas\u002F"," and shared between validation and TypeScript type inference.",[18,435,437],{"id":436},"document-security","Document Security",[23,439,440,450],{},[26,441,442],{},[29,443,444,447],{},[32,445,446],{},"Measure",[32,448,449],{},"Implementation",[39,451,452,462,472,486,499,512,522,536],{},[29,453,454,459],{},[44,455,456],{},[47,457,458],{},"Private S3 bucket",[44,460,461],{},"Policy enforced on every startup — deletes any public access rules",[29,463,464,469],{},[44,465,466],{},[47,467,468],{},"Server-proxied access",[44,470,471],{},"Download and preview endpoints stream from S3; no presigned URLs exposed",[29,473,474,479],{},[44,475,476],{},[47,477,478],{},"MIME validation",[44,480,481,482,485],{},"Magic byte inspection (not just ",[53,483,484],{},"Content-Type"," header)",[29,487,488,493],{},[44,489,490],{},[47,491,492],{},"Filename sanitization",[44,494,495,498],{},[53,496,497],{},"sanitizeFilename()"," strips path traversal, XSS payloads, unsafe characters",[29,500,501,506],{},[44,502,503],{},[47,504,505],{},"Storage key hidden",[44,507,508,511],{},[53,509,510],{},"storageKey"," (S3 object key) is filtered from all API responses",[29,513,514,519],{},[44,515,516],{},[47,517,518],{},"Document limits",[44,520,521],{},"Max 20 documents per candidate on public endpoints",[29,523,524,529],{},[44,525,526],{},[47,527,528],{},"PDF-only preview",[44,530,531,532,535],{},"Only ",[53,533,534],{},"application\u002Fpdf"," can be previewed inline",[29,537,538,543],{},[44,539,540],{},[47,541,542],{},"Cache headers",[44,544,545,548],{},[53,546,547],{},"Cache-Control: private, no-store"," on all document endpoints",[18,550,552],{"id":551},"rate-limiting","Rate Limiting",[14,554,555],{},"Public endpoints are protected by IP-based sliding window rate limiting:",[145,557,558,566,572,578],{},[148,559,560,562,563],{},[47,561,449],{},": ",[53,564,565],{},"server\u002Futils\u002FrateLimit.ts",[148,567,568,571],{},[47,569,570],{},"Scope",": Applied to public application submission and authentication endpoints",[148,573,574,577],{},[47,575,576],{},"Algorithm",": Sliding window per IP address",[148,579,580,583],{},[47,581,582],{},"Storage",": In-memory (single instance)",[18,585,587],{"id":586},"http-security-headers","HTTP Security Headers",[14,589,590],{},"Global security headers are applied via Nitro route rules:",[169,592,594],{"className":171,"code":593,"language":173,"meta":174,"style":174},"\u002F\u002F Applied to all routes\n{\n  'X-Content-Type-Options': 'nosniff',\n  'X-Frame-Options': 'DENY',\n  'Referrer-Policy': 'strict-origin-when-cross-origin',\n  'X-XSS-Protection': '1; mode=block',\n  'Permissions-Policy': 'camera=(), microphone=(), geolocation=()',\n}\n",[53,595,596,601,606,620,632,644,656,669],{"__ignoreMap":174},[178,597,598],{"class":180,"line":181},[178,599,600],{"class":184},"\u002F\u002F Applied to all routes\n",[178,602,603],{"class":180,"line":188},[178,604,605],{"class":202},"{\n",[178,607,608,612,614,617],{"class":180,"line":225},[178,609,611],{"class":610},"sZZnC","  'X-Content-Type-Options'",[178,613,562],{"class":202},[178,615,616],{"class":610},"'nosniff'",[178,618,619],{"class":202},",\n",[178,621,622,625,627,630],{"class":180,"line":259},[178,623,624],{"class":610},"  'X-Frame-Options'",[178,626,562],{"class":202},[178,628,629],{"class":610},"'DENY'",[178,631,619],{"class":202},[178,633,634,637,639,642],{"class":180,"line":273},[178,635,636],{"class":610},"  'Referrer-Policy'",[178,638,562],{"class":202},[178,640,641],{"class":610},"'strict-origin-when-cross-origin'",[178,643,619],{"class":202},[178,645,646,649,651,654],{"class":180,"line":279},[178,647,648],{"class":610},"  'X-XSS-Protection'",[178,650,562],{"class":202},[178,652,653],{"class":610},"'1; mode=block'",[178,655,619],{"class":202},[178,657,659,662,664,667],{"class":180,"line":658},7,[178,660,661],{"class":610},"  'Permissions-Policy'",[178,663,562],{"class":202},[178,665,666],{"class":610},"'camera=(), microphone=(), geolocation=()'",[178,668,619],{"class":202},[178,670,672],{"class":180,"line":671},8,[178,673,674],{"class":202},"}\n",[14,676,677,678,681],{},"Exception: Document preview endpoints use ",[53,679,680],{},"X-Frame-Options: SAMEORIGIN"," to allow inline PDF rendering in the application.",[18,683,685],{"id":684},"search-engine-isolation","Search Engine Isolation",[14,687,688],{},"Private pages are blocked from search engine crawling:",[145,690,691,697],{},[148,692,693,694],{},"Dashboard, auth, onboarding, and API routes are disallowed in ",[53,695,696],{},"robots.txt",[148,698,699,700,703,704],{},"Private pages include ",[53,701,702],{},"noindex, nofollow"," meta tags via ",[53,705,706],{},"useSeoMeta()",[18,708,710],{"id":709},"recommendations-for-self-hosting","Recommendations for Self-Hosting",[712,713,714,720,730,740,754],"ol",{},[148,715,716,719],{},[47,717,718],{},"Use HTTPS"," — Put a reverse proxy (Nginx, Caddy) with TLS in front of the application",[148,721,722,725,726,729],{},[47,723,724],{},"Change all default passwords"," — Run ",[53,727,728],{},"setup.sh"," to generate secrets",[148,731,732,735,736,739],{},[47,733,734],{},"Keep Docker ports local"," — Ports are bound to ",[53,737,738],{},"127.0.0.1"," by default; don't expose them",[148,741,742,745,746,749,750,753],{},[47,743,744],{},"Back up your data"," — Schedule ",[53,747,748],{},"pg_dump"," for PostgreSQL and ",[53,751,752],{},"mc mirror"," for S3",[148,755,756,759],{},[47,757,758],{},"Keep up to date"," — Pull the latest code and run migrations regularly",[18,761,763],{"id":762},"next-steps","Next Steps",[145,765,766,773,780],{},[148,767,768,772],{},[137,769,771],{"href":770},"\u002Fdocs\u002Farchitecture\u002Foverview","Architecture Overview"," — System design overview",[148,774,775,779],{},[137,776,778],{"href":777},"\u002Fdocs\u002Farchitecture\u002Fdata-model","Data Model"," — Database schema and relationships",[148,781,782,786],{},[137,783,785],{"href":784},"\u002Fdocs\u002Fdeployment\u002Fenvironment-variables","Environment Variables"," — Configuration reference",[788,789,790],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":174,"searchDepth":188,"depth":188,"links":792},[793,794,797,798,799,800,801,802,803,804],{"id":20,"depth":188,"text":21},{"id":132,"depth":188,"text":49,"children":795},[796],{"id":166,"depth":225,"text":167},{"id":330,"depth":188,"text":331},{"id":366,"depth":188,"text":367},{"id":436,"depth":188,"text":437},{"id":551,"depth":188,"text":552},{"id":586,"depth":188,"text":587},{"id":684,"depth":188,"text":685},{"id":709,"depth":188,"text":710},{"id":762,"depth":188,"text":763},"Security boundaries, authentication, authorization, and data protection in Reqcore. Covers tenant isolation, input validation, document security, and headers.","md","shield-check",{},"\u002Fdocs\u002Farchitecture\u002Fsecurity","Architecture",{"title":5,"description":805},"docs\u002F4.architecture\u002F4.security","Yln8QS9yiNfhujfEVOYDCJD-cHm2V3_w3IGlaSKqkas",[815,1919,2165,2504,3063,4159,4737,5204,5953,6340,6772,7131,7395,7794,8487,8921,9324,9885,10502,10821,11084],{"id":816,"title":778,"body":817,"description":1913,"extension":806,"icon":1914,"meta":1915,"navigation":403,"path":777,"section":810,"seo":1916,"stem":1917,"__hash__":1918},"docs\u002Fdocs\u002F4.architecture\u002F3.data-model.md",{"type":7,"value":818,"toc":1899},[819,822,836,840,846,850,855,858,1093,1098,1101,1212,1221,1226,1229,1366,1374,1379,1382,1522,1528,1531,1666,1672,1675,1743,1745,1758,1761,1816,1820,1823,1864,1875,1877,1896],[10,820,778],{"id":821},"data-model",[14,823,135,824,827,828,831,832,835],{},[47,825,826],{},"PostgreSQL 16"," with ",[47,829,830],{},"Drizzle ORM"," for type-safe database access. All domain tables are defined in ",[53,833,834],{},"server\u002Fdatabase\u002Fschema\u002Fapp.ts",".",[18,837,839],{"id":838},"entity-relationship","Entity Relationship",[169,841,844],{"className":842,"code":843,"language":347},[345],"organization (Better Auth)\n├── job (draft → open → closed → archived)\n│   ├── jobQuestion (custom application form questions)\n│   └── application (new → screening → interview → offer → hired\u002Frejected)\n│       ├── questionResponse (answers to custom questions)\n│       └── candidate (deduplicated by email per org)\n│           └── document (resume, cover_letter — stored in S3)\n└── member (user ↔ organization with role)\n",[53,845,843],{"__ignoreMap":174},[18,847,849],{"id":848},"tables","Tables",[164,851,853],{"id":852},"job",[53,854,852],{},[14,856,857],{},"The core hiring entity. Each job belongs to an organization.",[23,859,860,873],{},[26,861,862],{},[29,863,864,867,870],{},[32,865,866],{},"Column",[32,868,869],{},"Type",[32,871,872],{},"Description",[39,874,875,890,905,919,933,959,973,987,1001,1019,1033,1047,1061,1076],{},[29,876,877,882,887],{},[44,878,879],{},[53,880,881],{},"id",[44,883,884],{},[53,885,886],{},"uuid",[44,888,889],{},"Primary key",[29,891,892,897,902],{},[44,893,894],{},[53,895,896],{},"organizationId",[44,898,899],{},[53,900,901],{},"string",[44,903,904],{},"FK → organization",[29,906,907,912,916],{},[44,908,909],{},[53,910,911],{},"title",[44,913,914],{},[53,915,901],{},[44,917,918],{},"Job title",[29,920,921,926,930],{},[44,922,923],{},[53,924,925],{},"description",[44,927,928],{},[53,929,347],{},[44,931,932],{},"Full description (Markdown)",[29,934,935,940,945],{},[44,936,937],{},[53,938,939],{},"status",[44,941,942],{},[53,943,944],{},"enum",[44,946,947,238,950,238,953,238,956],{},[53,948,949],{},"draft",[53,951,952],{},"open",[53,954,955],{},"closed",[53,957,958],{},"archived",[29,960,961,966,970],{},[44,962,963],{},[53,964,965],{},"location",[44,967,968],{},[53,969,901],{},[44,971,972],{},"Office location",[29,974,975,980,984],{},[44,976,977],{},[53,978,979],{},"employmentType",[44,981,982],{},[53,983,901],{},[44,985,986],{},"Full-time, Part-time, etc.",[29,988,989,994,998],{},[44,990,991],{},[53,992,993],{},"slug",[44,995,996],{},[53,997,901],{},[44,999,1000],{},"URL slug for public page",[29,1002,1003,1011,1016],{},[44,1004,1005,93,1008],{},[53,1006,1007],{},"salaryMin",[53,1009,1010],{},"salaryMax",[44,1012,1013],{},[53,1014,1015],{},"integer",[44,1017,1018],{},"Salary range",[29,1020,1021,1026,1030],{},[44,1022,1023],{},[53,1024,1025],{},"salaryCurrency",[44,1027,1028],{},[53,1029,901],{},[44,1031,1032],{},"Currency code (USD, EUR, etc.)",[29,1034,1035,1040,1044],{},[44,1036,1037],{},[53,1038,1039],{},"salaryUnit",[44,1041,1042],{},[53,1043,901],{},[44,1045,1046],{},"HOUR, DAY, WEEK, MONTH, YEAR",[29,1048,1049,1054,1058],{},[44,1050,1051],{},[53,1052,1053],{},"remoteStatus",[44,1055,1056],{},[53,1057,901],{},[44,1059,1060],{},"On-site, Remote, Hybrid",[29,1062,1063,1068,1073],{},[44,1064,1065],{},[53,1066,1067],{},"validThrough",[44,1069,1070],{},[53,1071,1072],{},"timestamp",[44,1074,1075],{},"Application deadline",[29,1077,1078,1086,1090],{},[44,1079,1080,93,1083],{},[53,1081,1082],{},"createdAt",[53,1084,1085],{},"updatedAt",[44,1087,1088],{},[53,1089,1072],{},[44,1091,1092],{},"Timestamps",[164,1094,1096],{"id":1095},"candidate",[53,1097,1095],{},[14,1099,1100],{},"A person who has applied or been added to the talent pool.",[23,1102,1103,1113],{},[26,1104,1105],{},[29,1106,1107,1109,1111],{},[32,1108,866],{},[32,1110,869],{},[32,1112,872],{},[39,1114,1115,1127,1139,1156,1170,1184,1198],{},[29,1116,1117,1121,1125],{},[44,1118,1119],{},[53,1120,881],{},[44,1122,1123],{},[53,1124,886],{},[44,1126,889],{},[29,1128,1129,1133,1137],{},[44,1130,1131],{},[53,1132,896],{},[44,1134,1135],{},[53,1136,901],{},[44,1138,904],{},[29,1140,1141,1149,1153],{},[44,1142,1143,93,1146],{},[53,1144,1145],{},"firstName",[53,1147,1148],{},"lastName",[44,1150,1151],{},[53,1152,901],{},[44,1154,1155],{},"Full name",[29,1157,1158,1163,1167],{},[44,1159,1160],{},[53,1161,1162],{},"email",[44,1164,1165],{},[53,1166,901],{},[44,1168,1169],{},"Email (unique per org)",[29,1171,1172,1177,1181],{},[44,1173,1174],{},[53,1175,1176],{},"phone",[44,1178,1179],{},[53,1180,901],{},[44,1182,1183],{},"Phone number",[29,1185,1186,1191,1195],{},[44,1187,1188],{},[53,1189,1190],{},"notes",[44,1192,1193],{},[53,1194,347],{},[44,1196,1197],{},"Recruiter notes",[29,1199,1200,1206,1210],{},[44,1201,1202,93,1204],{},[53,1203,1082],{},[53,1205,1085],{},[44,1207,1208],{},[53,1209,1072],{},[44,1211,1092],{},[14,1213,1214,562,1217,1220],{},[47,1215,1216],{},"Unique constraint",[53,1218,1219],{},"(organizationId, email)"," — prevents duplicate candidates within an org.",[164,1222,1224],{"id":1223},"application",[53,1225,1223],{},[14,1227,1228],{},"Links a candidate to a job with a pipeline status.",[23,1230,1231,1241],{},[26,1232,1233],{},[29,1234,1235,1237,1239],{},[32,1236,866],{},[32,1238,869],{},[32,1240,872],{},[39,1242,1243,1255,1267,1281,1295,1325,1338,1352],{},[29,1244,1245,1249,1253],{},[44,1246,1247],{},[53,1248,881],{},[44,1250,1251],{},[53,1252,886],{},[44,1254,889],{},[29,1256,1257,1261,1265],{},[44,1258,1259],{},[53,1260,896],{},[44,1262,1263],{},[53,1264,901],{},[44,1266,904],{},[29,1268,1269,1274,1278],{},[44,1270,1271],{},[53,1272,1273],{},"jobId",[44,1275,1276],{},[53,1277,886],{},[44,1279,1280],{},"FK → job",[29,1282,1283,1288,1292],{},[44,1284,1285],{},[53,1286,1287],{},"candidateId",[44,1289,1290],{},[53,1291,886],{},[44,1293,1294],{},"FK → candidate",[29,1296,1297,1301,1305],{},[44,1298,1299],{},[53,1300,939],{},[44,1302,1303],{},[53,1304,944],{},[44,1306,1307,238,1310,238,1313,238,1316,238,1319,238,1322],{},[53,1308,1309],{},"new",[53,1311,1312],{},"screening",[53,1314,1315],{},"interview",[53,1317,1318],{},"offer",[53,1320,1321],{},"hired",[53,1323,1324],{},"rejected",[29,1326,1327,1331,1335],{},[44,1328,1329],{},[53,1330,1190],{},[44,1332,1333],{},[53,1334,347],{},[44,1336,1337],{},"Application-specific notes",[29,1339,1340,1345,1349],{},[44,1341,1342],{},[53,1343,1344],{},"score",[44,1346,1347],{},[53,1348,1015],{},[44,1350,1351],{},"Recruiter-assigned score",[29,1353,1354,1360,1364],{},[44,1355,1356,93,1358],{},[53,1357,1082],{},[53,1359,1085],{},[44,1361,1362],{},[53,1363,1072],{},[44,1365,1092],{},[14,1367,1368,562,1370,1373],{},[47,1369,1216],{},[53,1371,1372],{},"(organizationId, candidateId, jobId)"," — one application per candidate per job.",[164,1375,1377],{"id":1376},"document",[53,1378,1376],{},[14,1380,1381],{},"A file (resume, cover letter, work sample) stored in S3.",[23,1383,1384,1394],{},[26,1385,1386],{},[29,1387,1388,1390,1392],{},[32,1389,866],{},[32,1391,869],{},[32,1393,872],{},[39,1395,1396,1408,1420,1432,1446,1460,1474,1487,1509],{},[29,1397,1398,1402,1406],{},[44,1399,1400],{},[53,1401,881],{},[44,1403,1404],{},[53,1405,886],{},[44,1407,889],{},[29,1409,1410,1414,1418],{},[44,1411,1412],{},[53,1413,896],{},[44,1415,1416],{},[53,1417,901],{},[44,1419,904],{},[29,1421,1422,1426,1430],{},[44,1423,1424],{},[53,1425,1287],{},[44,1427,1428],{},[53,1429,886],{},[44,1431,1294],{},[29,1433,1434,1439,1443],{},[44,1435,1436],{},[53,1437,1438],{},"fileName",[44,1440,1441],{},[53,1442,901],{},[44,1444,1445],{},"Original filename (sanitized)",[29,1447,1448,1453,1457],{},[44,1449,1450],{},[53,1451,1452],{},"mimeType",[44,1454,1455],{},[53,1456,901],{},[44,1458,1459],{},"File MIME type",[29,1461,1462,1467,1471],{},[44,1463,1464],{},[53,1465,1466],{},"fileSize",[44,1468,1469],{},[53,1470,1015],{},[44,1472,1473],{},"File size in bytes",[29,1475,1476,1480,1484],{},[44,1477,1478],{},[53,1479,510],{},[44,1481,1482],{},[53,1483,901],{},[44,1485,1486],{},"S3 object key (never exposed to clients)",[29,1488,1489,1494,1498],{},[44,1490,1491],{},[53,1492,1493],{},"documentType",[44,1495,1496],{},[53,1497,944],{},[44,1499,1500,238,1503,238,1506],{},[53,1501,1502],{},"resume",[53,1504,1505],{},"cover_letter",[53,1507,1508],{},"other",[29,1510,1511,1515,1519],{},[44,1512,1513],{},[53,1514,1082],{},[44,1516,1517],{},[53,1518,1072],{},[44,1520,1521],{},"Upload timestamp",[164,1523,1525],{"id":1524},"jobquestion",[53,1526,1527],{},"jobQuestion",[14,1529,1530],{},"Custom application form questions per job.",[23,1532,1533,1543],{},[26,1534,1535],{},[29,1536,1537,1539,1541],{},[32,1538,866],{},[32,1540,869],{},[32,1542,872],{},[39,1544,1545,1557,1569,1583,1622,1637,1652],{},[29,1546,1547,1551,1555],{},[44,1548,1549],{},[53,1550,881],{},[44,1552,1553],{},[53,1554,886],{},[44,1556,889],{},[29,1558,1559,1563,1567],{},[44,1560,1561],{},[53,1562,1273],{},[44,1564,1565],{},[53,1566,886],{},[44,1568,1280],{},[29,1570,1571,1576,1580],{},[44,1572,1573],{},[53,1574,1575],{},"questionText",[44,1577,1578],{},[53,1579,901],{},[44,1581,1582],{},"Question text",[29,1584,1585,1590,1594],{},[44,1586,1587],{},[53,1588,1589],{},"questionType",[44,1591,1592],{},[53,1593,944],{},[44,1595,1596,238,1599,238,1602,238,1605,238,1608,238,1611,238,1614,238,1617,238,1619],{},[53,1597,1598],{},"short_text",[53,1600,1601],{},"long_text",[53,1603,1604],{},"single_select",[53,1606,1607],{},"multi_select",[53,1609,1610],{},"number",[53,1612,1613],{},"date",[53,1615,1616],{},"url",[53,1618,1162],{},[53,1620,1621],{},"file_upload",[29,1623,1624,1629,1634],{},[44,1625,1626],{},[53,1627,1628],{},"required",[44,1630,1631],{},[53,1632,1633],{},"boolean",[44,1635,1636],{},"Whether answer is required",[29,1638,1639,1644,1649],{},[44,1640,1641],{},[53,1642,1643],{},"options",[44,1645,1646],{},[53,1647,1648],{},"json",[44,1650,1651],{},"Options for select types",[29,1653,1654,1659,1663],{},[44,1655,1656],{},[53,1657,1658],{},"sortOrder",[44,1660,1661],{},[53,1662,1015],{},[44,1664,1665],{},"Display order",[164,1667,1669],{"id":1668},"questionresponse",[53,1670,1671],{},"questionResponse",[14,1673,1674],{},"Answers to custom questions per application.",[23,1676,1677,1687],{},[26,1678,1679],{},[29,1680,1681,1683,1685],{},[32,1682,866],{},[32,1684,869],{},[32,1686,872],{},[39,1688,1689,1701,1715,1729],{},[29,1690,1691,1695,1699],{},[44,1692,1693],{},[53,1694,881],{},[44,1696,1697],{},[53,1698,886],{},[44,1700,889],{},[29,1702,1703,1708,1712],{},[44,1704,1705],{},[53,1706,1707],{},"applicationId",[44,1709,1710],{},[53,1711,886],{},[44,1713,1714],{},"FK → application",[29,1716,1717,1722,1726],{},[44,1718,1719],{},[53,1720,1721],{},"questionId",[44,1723,1724],{},[53,1725,886],{},[44,1727,1728],{},"FK → jobQuestion",[29,1730,1731,1736,1740],{},[44,1732,1733],{},[53,1734,1735],{},"responseValue",[44,1737,1738],{},[53,1739,347],{},[44,1741,1742],{},"The answer",[18,1744,331],{"id":330},[14,1746,1747,1748,1750,1751,1757],{},"Every domain table includes an ",[53,1749,896],{}," column. ",[47,1752,1753,1754,1756],{},"All database queries must include an ",[53,1755,896],{}," filter"," derived from the authenticated session — never from user input.",[14,1759,1760],{},"Example query pattern:",[169,1762,1764],{"className":171,"code":1763,"language":173,"meta":174,"style":174},"const jobs = await db\n  .select()\n  .from(job)\n  .where(eq(job.organizationId, orgId))\n",[53,1765,1766,1780,1791,1801],{"__ignoreMap":174},[178,1767,1768,1770,1773,1775,1777],{"class":180,"line":181},[178,1769,385],{"class":191},[178,1771,1772],{"class":234}," jobs",[178,1774,267],{"class":191},[178,1776,250],{"class":191},[178,1778,1779],{"class":202}," db\n",[178,1781,1782,1785,1788],{"class":180,"line":188},[178,1783,1784],{"class":202},"  .",[178,1786,1787],{"class":198},"select",[178,1789,1790],{"class":202},"()\n",[178,1792,1793,1795,1798],{"class":180,"line":225},[178,1794,1784],{"class":202},[178,1796,1797],{"class":198},"from",[178,1799,1800],{"class":202},"(job)\n",[178,1802,1803,1805,1808,1810,1813],{"class":180,"line":259},[178,1804,1784],{"class":202},[178,1806,1807],{"class":198},"where",[178,1809,203],{"class":202},[178,1811,1812],{"class":198},"eq",[178,1814,1815],{"class":202},"(job.organizationId, orgId))\n",[18,1817,1819],{"id":1818},"migrations","Migrations",[14,1821,1822],{},"Database migrations are managed by Drizzle Kit:",[169,1824,1828],{"className":1825,"code":1826,"language":1827,"meta":174,"style":174},"language-bash shiki shiki-themes github-light github-dark","# Generate a migration after schema changes\nnpm run db:generate\n\n# Apply migrations manually\nnpm run db:migrate\n","bash",[53,1829,1830,1835,1846,1850,1855],{"__ignoreMap":174},[178,1831,1832],{"class":180,"line":181},[178,1833,1834],{"class":184},"# Generate a migration after schema changes\n",[178,1836,1837,1840,1843],{"class":180,"line":188},[178,1838,1839],{"class":198},"npm",[178,1841,1842],{"class":610}," run",[178,1844,1845],{"class":610}," db:generate\n",[178,1847,1848],{"class":180,"line":225},[178,1849,404],{"emptyLinePlaceholder":403},[178,1851,1852],{"class":180,"line":259},[178,1853,1854],{"class":184},"# Apply migrations manually\n",[178,1856,1857,1859,1861],{"class":180,"line":273},[178,1858,1839],{"class":198},[178,1860,1842],{"class":610},[178,1862,1863],{"class":610}," db:migrate\n",[14,1865,1866,1867,1870,1871,1874],{},"Migrations also run ",[47,1868,1869],{},"automatically on server startup"," via the ",[53,1872,1873],{},"server\u002Fplugins\u002Fmigrations.ts"," plugin, using a PostgreSQL advisory lock for safety in multi-instance deployments.",[18,1876,763],{"id":762},[145,1878,1879,1884,1889],{},[148,1880,1881,1883],{},[137,1882,5],{"href":809}," — Security boundaries and enforcement",[148,1885,1886,1888],{},[137,1887,771],{"href":770}," — High-level system design",[148,1890,1891,1895],{},[137,1892,1894],{"href":1893},"\u002Fdocs\u002Farchitecture\u002Fdirectory-structure","Directory Structure"," — Project file organization",[788,1897,1898],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":174,"searchDepth":188,"depth":188,"links":1900},[1901,1902,1910,1911,1912],{"id":838,"depth":188,"text":839},{"id":848,"depth":188,"text":849,"children":1903},[1904,1905,1906,1907,1908,1909],{"id":852,"depth":225,"text":852},{"id":1095,"depth":225,"text":1095},{"id":1223,"depth":225,"text":1223},{"id":1376,"depth":225,"text":1376},{"id":1524,"depth":225,"text":1527},{"id":1668,"depth":225,"text":1671},{"id":330,"depth":188,"text":331},{"id":1818,"depth":188,"text":1819},{"id":762,"depth":188,"text":763},"Database schema and entity relationships for Reqcore. Covers jobs, candidates, applications, documents, and multi-tenant isolation.","database",{},{"title":778,"description":1913},"docs\u002F4.architecture\u002F3.data-model","d5y8Q5UZ_dowxkib6bFuFal80mLGHJa-fPl_BDUVfKg",{"id":1920,"title":1894,"body":1921,"description":2159,"extension":806,"icon":2160,"meta":2161,"navigation":403,"path":1893,"section":810,"seo":2162,"stem":2163,"__hash__":2164},"docs\u002Fdocs\u002F4.architecture\u002F2.directory-structure.md",{"type":7,"value":1922,"toc":2149},[1923,1926,1937,1941,1947,1951,1959,1968,1995,1999,2002,2083,2087,2097,2131,2133],[10,1924,1894],{"id":1925},"directory-structure",[14,1927,1928,1929,1932,1933,1936],{},"Reqcore follows the ",[47,1930,1931],{},"Nuxt 4 directory convention"," with all application source code in ",[53,1934,1935],{},"app\u002F"," and server code at the project root.",[18,1938,1940],{"id":1939},"full-layout","Full Layout",[169,1942,1945],{"className":1943,"code":1944,"language":347},[345],"reqcore\u002F\n├── app\u002F                          # Client source (Nuxt 4 srcDir)\n│   ├── app.vue                   # Root component\n│   ├── assets\u002F\n│   │   └── css\u002Fmain.css          # Tailwind CSS entry + @theme tokens\n│   ├── components\u002F               # Auto-imported Vue components\n│   ├── composables\u002F              # Auto-imported composables (useXxx)\n│   ├── layouts\u002F                  # Layout components\n│   │   ├── dashboard.vue         # Sidebar + main content\n│   │   ├── auth.vue              # Centered card for auth pages\n│   │   └── public.vue            # Simple header\u002Ffooter for public pages\n│   ├── middleware\u002F                # Client-side route middleware\n│   ├── pages\u002F                    # File-based routing\n│   │   ├── index.vue             # Landing page (dark theme)\n│   │   ├── roadmap.vue           # Public roadmap\n│   │   ├── blog\u002F                 # Blog listing + detail\n│   │   ├── docs\u002F                 # Documentation listing + detail\n│   │   ├── catalog\u002F              # Feature catalog\n│   │   ├── jobs\u002F                 # Public job board\n│   │   ├── auth\u002F                 # Sign-in \u002F sign-up\n│   │   ├── dashboard\u002F            # Authenticated dashboard\n│   │   └── onboarding\u002F           # Org creation flow\n│   ├── plugins\u002F                  # Client-side Nuxt plugins\n│   └── utils\u002F                    # Auto-imported utilities\n│       └── auth-client.ts        # Better Auth Vue client\n├── server\u002F                       # Nitro server (at project root)\n│   ├── api\u002F                      # API route handlers\n│   │   ├── auth\u002F[...all].ts      # Better Auth catch-all\n│   │   ├── jobs\u002F                 # Job CRUD + questions\n│   │   ├── candidates\u002F           # Candidate CRUD + documents\n│   │   ├── applications\u002F         # Application CRUD\n│   │   ├── documents\u002F            # Document access endpoints\n│   │   ├── dashboard\u002F            # Dashboard stats\n│   │   └── public\u002Fjobs\u002F          # Unauthenticated job board API\n│   ├── database\u002F\n│   │   ├── schema\u002F               # Drizzle ORM table definitions\n│   │   │   ├── app.ts            # Domain tables (job, candidate, etc.)\n│   │   │   ├── auth.ts           # Better Auth tables (DO NOT MODIFY)\n│   │   │   └── index.ts          # Re-exports\n│   │   └── migrations\u002F           # Generated SQL migrations\n│   ├── middleware\u002F                # Global server middleware\n│   ├── plugins\u002F\n│   │   ├── migrations.ts         # Auto-apply migrations on startup\n│   │   └── s3-bucket.ts          # Ensure S3 bucket exists + enforce private policy\n│   └── utils\u002F                    # Auto-imported server utilities\n│       ├── auth.ts               # Better Auth instance\n│       ├── db.ts                 # Drizzle client + connection pool\n│       ├── env.ts                # Zod-validated environment variables\n│       ├── requireAuth.ts        # Auth guard (throws 401\u002F403)\n│       ├── s3.ts                 # S3\u002FMinIO client, upload, delete\n│       ├── slugify.ts            # URL slug generation\n│       ├── rateLimit.ts          # IP-based sliding window rate limiter\n│       └── schemas\u002F              # Zod validation schemas\n├── content\u002F                      # @nuxt\u002Fcontent v3 collections\n│   ├── blog\u002F                     # Blog articles (Markdown)\n│   ├── catalog\u002F                  # Feature catalog items\n│   └── docs\u002F                     # Documentation pages\n├── shared\u002F                       # Shared between app and server\n├── public\u002F                       # Static assets (favicon, images)\n├── i18n\u002F                         # Internationalization\n│   └── locales\u002F                  # Translation JSON files\n├── e2e\u002F                          # Playwright end-to-end tests\n├── nuxt.config.ts                # Nuxt configuration\n├── content.config.ts             # Content collection definitions\n├── drizzle.config.ts             # Drizzle Kit configuration\n├── docker-compose.yml            # PostgreSQL + MinIO + Adminer\n└── package.json\n",[53,1946,1944],{"__ignoreMap":174},[18,1948,1950],{"id":1949},"key-conventions","Key Conventions",[164,1952,1954,1955,1958],{"id":1953},"the-alias","The ",[53,1956,1957],{},"~"," Alias",[14,1960,1961,1962,1964,1965,1967],{},"In Nuxt 4, ",[53,1963,1957],{}," resolves to the ",[53,1966,1935],{}," directory:",[145,1969,1970,1979,1987],{},[148,1971,1972,1975,1976],{},[53,1973,1974],{},"~\u002Fcomponents"," → ",[53,1977,1978],{},"app\u002Fcomponents\u002F",[148,1980,1981,1975,1984],{},[53,1982,1983],{},"~\u002Fcomposables",[53,1985,1986],{},"app\u002Fcomposables\u002F",[148,1988,1989,1975,1992],{},[53,1990,1991],{},"~\u002Fassets",[53,1993,1994],{},"app\u002Fassets\u002F",[164,1996,1998],{"id":1997},"auto-imports","Auto-Imports",[14,2000,2001],{},"Nuxt auto-imports files from these directories:",[23,2003,2004,2017],{},[26,2005,2006],{},[29,2007,2008,2011,2014],{},[32,2009,2010],{},"Directory",[32,2012,2013],{},"Context",[32,2015,2016],{},"Import Pattern",[39,2018,2019,2031,2045,2057,2070],{},[29,2020,2021,2025,2028],{},[44,2022,2023],{},[53,2024,1978],{},[44,2026,2027],{},"Client",[44,2029,2030],{},"Components used in templates",[29,2032,2033,2037,2039],{},[44,2034,2035],{},[53,2036,1986],{},[44,2038,2027],{},[44,2040,2041,2044],{},[53,2042,2043],{},"useXxx()"," functions",[29,2046,2047,2052,2054],{},[44,2048,2049],{},[53,2050,2051],{},"app\u002Futils\u002F",[44,2053,2027],{},[44,2055,2056],{},"Utility functions",[29,2058,2059,2064,2067],{},[44,2060,2061],{},[53,2062,2063],{},"server\u002Futils\u002F",[44,2065,2066],{},"Server",[44,2068,2069],{},"Server-side utilities",[29,2071,2072,2077,2080],{},[44,2073,2074],{},[53,2075,2076],{},"shared\u002F",[44,2078,2079],{},"Both",[44,2081,2082],{},"Shared types and functions",[164,2084,2086],{"id":2085},"root-level-directories","Root-Level Directories",[14,2088,2089,2090,2093,2094,2096],{},"These directories stay at the project root — ",[47,2091,2092],{},"not"," inside ",[53,2095,1935],{},":",[145,2098,2099,2105,2111,2120,2125],{},[148,2100,2101,2104],{},[53,2102,2103],{},"server\u002F"," — All Nitro server code",[148,2106,2107,2110],{},[53,2108,2109],{},"content\u002F"," — Markdown content for @nuxt\u002Fcontent",[148,2112,2113,2116,2117],{},[53,2114,2115],{},"public\u002F"," — Static files served at ",[53,2118,2119],{},"\u002F",[148,2121,2122,2124],{},[53,2123,2076],{}," — Code shared between app and server",[148,2126,2127,2130],{},[53,2128,2129],{},"i18n\u002F"," — Translation files",[18,2132,763],{"id":762},[145,2134,2135,2140,2144],{},[148,2136,2137,2139],{},[137,2138,778],{"href":777}," — Database tables and relationships",[148,2141,2142,1888],{},[137,2143,771],{"href":770},[148,2145,2146,2148],{},[137,2147,5],{"href":809}," — Security boundaries",{"title":174,"searchDepth":188,"depth":188,"links":2150},[2151,2152,2158],{"id":1939,"depth":188,"text":1940},{"id":1949,"depth":188,"text":1950,"children":2153},[2154,2156,2157],{"id":1953,"depth":225,"text":2155},"The ~ Alias",{"id":1997,"depth":225,"text":1998},{"id":2085,"depth":225,"text":2086},{"id":762,"depth":188,"text":763},"Nuxt 4 project layout for Reqcore with app, server, content, and shared directories explained.","folder-tree",{},{"title":1894,"description":2159},"docs\u002F4.architecture\u002F2.directory-structure","poUgDixffJe5w1y3e0e8QFLM7zSzQ2mGDbrHsvYeKnE",{"id":2166,"title":2167,"body":2168,"description":2498,"extension":806,"icon":2499,"meta":2500,"navigation":403,"path":770,"section":810,"seo":2501,"stem":2502,"__hash__":2503},"docs\u002Fdocs\u002F4.architecture\u002F1.overview.md","Overview",{"type":7,"value":2169,"toc":2484},[2170,2173,2186,2190,2196,2200,2204,2219,2224,2233,2237,2243,2320,2324,2335,2339,2348,2352,2363,2367,2370,2372,2378,2381,2385,2467,2469],[10,2171,771],{"id":2172},"architecture-overview",[14,2174,2175,2176,2179,2180,2182,2183,2185],{},"Reqcore is a ",[47,2177,2178],{},"Nuxt 4"," full-stack monolith with clear separation between client (",[53,2181,1935],{},") and server (",[53,2184,2103],{},") code. The system supports both managed deployment on Railway and self-hosted deployment via Docker Compose.",[18,2187,2189],{"id":2188},"high-level-architecture","High-Level Architecture",[169,2191,2194],{"className":2192,"code":2193,"language":347},[345],"┌─────────────────────────────────────────────────────┐\n│                    Browser                           │\n│  ┌──────────────────────────────────────────────┐   │\n│  │  Nuxt App (Vue 3 + SSR)                      │   │\n│  │  • Pages \u002F Components \u002F Composables          │   │\n│  │  • Better Auth Vue Client                    │   │\n│  │  • useFetch \u002F $fetch → \u002Fapi\u002F*                │   │\n│  └──────────────────┬───────────────────────────┘   │\n└─────────────────────┼───────────────────────────────┘\n                      │ HTTPS\n┌─────────────────────┼───────────────────────────────┐\n│  Cloudflare CDN     │                                │\n│  • DNS, DDoS protection, edge caching               │\n└─────────────────────┼───────────────────────────────┘\n                      │ HTTPS\n┌─────────────────────┼───────────────────────────────┐\n│  Application Server                                  │\n│  ┌──────────────────▼───────────────────────────┐   │\n│  │  Nuxt \u002F Nitro SSR Server                      │   │\n│  └──────────┬───────────────────┬───────────────┘   │\n│             │                   │                    │\n│  ┌──────────▼──────┐   ┌───────▼────────────────┐   │\n│  │  PostgreSQL 16  │   │  S3-Compatible Storage │   │\n│  │  (Drizzle ORM)  │   │  (MinIO \u002F Railway)     │   │\n│  └─────────────────┘   └────────────────────────┘   │\n└─────────────────────────────────────────────────────┘\n",[53,2195,2193],{"__ignoreMap":174},[18,2197,2199],{"id":2198},"key-architectural-decisions","Key Architectural Decisions",[164,2201,2203],{"id":2202},"_1-multi-tenancy-via-organization-plugin","1. Multi-Tenancy via Organization Plugin",[14,2205,2206,2207,238,2209,238,2211,238,2213,2215,2216,2218],{},"All domain tables (",[53,2208,852],{},[53,2210,1095],{},[53,2212,1223],{},[53,2214,1376],{},") have an ",[53,2217,896],{}," foreign key. Tenant isolation is enforced at the application layer:",[169,2220,2222],{"className":2221,"code":346,"language":347},[345],[53,2223,346],{"__ignoreMap":174},[14,2225,2226,2229,2230,835],{},[47,2227,2228],{},"The org ID never comes from user input"," (body, query, URL params). It is always derived from the authenticated session's ",[53,2231,2232],{},"activeOrganizationId",[164,2234,2236],{"id":2235},"_2-auto-imported-server-utilities","2. Auto-Imported Server Utilities",[14,2238,2239,2240,2242],{},"Nitro auto-imports everything from ",[53,2241,2063],{},". Core utilities available without imports:",[23,2244,2245,2255],{},[26,2246,2247],{},[29,2248,2249,2252],{},[32,2250,2251],{},"Utility",[32,2253,2254],{},"Purpose",[39,2256,2257,2267,2277,2287,2297,2307],{},[29,2258,2259,2264],{},[44,2260,2261],{},[53,2262,2263],{},"db",[44,2265,2266],{},"Drizzle ORM client with schema",[29,2268,2269,2274],{},[44,2270,2271],{},[53,2272,2273],{},"auth",[44,2275,2276],{},"Better Auth instance",[29,2278,2279,2284],{},[44,2280,2281],{},[53,2282,2283],{},"env",[44,2285,2286],{},"Zod-validated environment variables",[29,2288,2289,2294],{},[44,2290,2291],{},[53,2292,2293],{},"generateJobSlug",[44,2295,2296],{},"URL slug generation for public job pages",[29,2298,2299,2304],{},[44,2300,2301],{},[53,2302,2303],{},"createRateLimiter",[44,2305,2306],{},"IP-based sliding window rate limiter",[29,2308,2309,2317],{},[44,2310,2311,93,2314],{},[53,2312,2313],{},"uploadToS3",[53,2315,2316],{},"deleteFromS3",[44,2318,2319],{},"S3\u002FMinIO file operations",[164,2321,2323],{"id":2322},"_3-environment-validation","3. Environment Validation",[14,2325,2326,2327,2330,2331,2334],{},"All environment variables are validated at startup via Zod in ",[53,2328,2329],{},"server\u002Futils\u002Fenv.ts",". If any variable is missing or malformed, the server fails immediately. No runtime ",[53,2332,2333],{},"process.env"," access is allowed outside this file.",[164,2336,2338],{"id":2337},"_4-auto-applied-migrations","4. Auto-Applied Migrations",[14,2340,1954,2341,2343,2344,2347],{},[53,2342,1873],{}," plugin runs Drizzle migrations automatically on server startup. It uses a PostgreSQL advisory lock (",[53,2345,2346],{},"pg_try_advisory_lock",") to prevent race conditions in multi-instance deployments.",[164,2349,2351],{"id":2350},"_5-ssr-cookie-forwarding","5. SSR + Cookie Forwarding",[14,2353,2354,2355,2358,2359,2362],{},"During server-side rendering, browser cookies are not automatically forwarded to internal API calls. All authenticated ",[53,2356,2357],{},"useFetch"," calls must include ",[53,2360,2361],{},"headers: useRequestHeaders(['cookie'])"," to forward the session cookie.",[164,2364,2366],{"id":2365},"_6-server-proxied-documents","6. Server-Proxied Documents",[14,2368,2369],{},"Documents are always accessed through authenticated server endpoints that stream bytes from S3. Presigned URLs are never exposed to clients, preventing URL sharing or leakage of sensitive candidate data.",[18,2371,778],{"id":821},[169,2373,2376],{"className":2374,"code":2375,"language":347},[345],"organization (Better Auth)\n├── job (draft → open → closed → archived)\n│   └── application (new → screening → interview → offer → hired\u002Frejected)\n│       └── candidate\n│           └── document (resume, cover_letter — stored in S3)\n└── member (user ↔ organization with role)\n",[53,2377,2375],{"__ignoreMap":174},[14,2379,2380],{},"All domain tables belong to exactly one organization. Candidates are deduplicated within each org by email.",[18,2382,2384],{"id":2383},"public-vs-authenticated-routes","Public vs Authenticated Routes",[23,2386,2387,2402],{},[26,2388,2389],{},[29,2390,2391,2393,2396,2399],{},[32,2392,869],{},[32,2394,2395],{},"Server Path",[32,2397,2398],{},"Page Path",[32,2400,2401],{},"Auth Required",[39,2403,2404,2420,2437,2453],{},[29,2405,2406,2409,2414,2417],{},[44,2407,2408],{},"Public API",[44,2410,2411],{},[53,2412,2413],{},"server\u002Fapi\u002Fpublic\u002F",[44,2415,2416],{},"—",[44,2418,2419],{},"No",[29,2421,2422,2425,2427,2435],{},[44,2423,2424],{},"Public pages",[44,2426,2416],{},[44,2428,2429,238,2432],{},[53,2430,2431],{},"app\u002Fpages\u002Fjobs\u002F",[53,2433,2434],{},"app\u002Fpages\u002Fblog\u002F",[44,2436,2419],{},[29,2438,2439,2442,2448,2450],{},[44,2440,2441],{},"Authenticated API",[44,2443,2444,2447],{},[53,2445,2446],{},"server\u002Fapi\u002F"," (all other)",[44,2449,2416],{},[44,2451,2452],{},"Yes",[29,2454,2455,2458,2460,2465],{},[44,2456,2457],{},"Dashboard pages",[44,2459,2416],{},[44,2461,2462],{},[53,2463,2464],{},"app\u002Fpages\u002Fdashboard\u002F",[44,2466,2452],{},[18,2468,763],{"id":762},[145,2470,2471,2476,2480],{},[148,2472,2473,2475],{},[137,2474,1894],{"href":1893}," — Detailed file organization",[148,2477,2478,779],{},[137,2479,778],{"href":777},[148,2481,2482,1883],{},[137,2483,5],{"href":809},{"title":174,"searchDepth":188,"depth":188,"links":2485},[2486,2487,2495,2496,2497],{"id":2188,"depth":188,"text":2189},{"id":2198,"depth":188,"text":2199,"children":2488},[2489,2490,2491,2492,2493,2494],{"id":2202,"depth":225,"text":2203},{"id":2235,"depth":225,"text":2236},{"id":2322,"depth":225,"text":2323},{"id":2337,"depth":225,"text":2338},{"id":2350,"depth":225,"text":2351},{"id":2365,"depth":225,"text":2366},{"id":821,"depth":188,"text":778},{"id":2383,"depth":188,"text":2384},{"id":762,"depth":188,"text":763},"System architecture overview for Reqcore — a Nuxt 4 full-stack application with PostgreSQL, S3 storage, and multi-tenant data isolation.","layers",{},{"title":2167,"description":2498},"docs\u002F4.architecture\u002F1.overview","wqe0s50B4wTV31tuKnMmHTdmnMpODsl8oxWjUNzfJGk",{"id":4,"title":5,"body":2505,"description":805,"extension":806,"icon":807,"meta":3061,"navigation":403,"path":809,"section":810,"seo":3062,"stem":812,"__hash__":813},{"type":7,"value":2506,"toc":3047},[2507,2509,2511,2513,2591,2593,2598,2610,2612,2682,2720,2722,2728,2733,2735,2743,2745,2747,2791,2795,2797,2883,2885,2887,2907,2909,2911,2977,2981,2983,2985,2997,2999,3029,3031,3045],[10,2508,5],{"id":12},[14,2510,16],{},[18,2512,21],{"id":20},[23,2514,2515,2523],{},[26,2516,2517],{},[29,2518,2519,2521],{},[32,2520,34],{},[32,2522,37],{},[39,2524,2525,2535,2543,2553,2565,2575,2583],{},[29,2526,2527,2531],{},[44,2528,2529],{},[47,2530,49],{},[44,2532,2533,56],{},[53,2534,55],{},[29,2536,2537,2541],{},[44,2538,2539],{},[47,2540,63],{},[44,2542,66],{},[29,2544,2545,2549],{},[44,2546,2547],{},[47,2548,73],{},[44,2550,76,2551],{},[53,2552,79],{},[29,2554,2555,2559],{},[44,2556,2557],{},[47,2558,86],{},[44,2560,89,2561,93,2563],{},[53,2562,92],{},[53,2564,96],{},[29,2566,2567,2571],{},[44,2568,2569],{},[47,2570,103],{},[44,2572,2573,109],{},[53,2574,108],{},[29,2576,2577,2581],{},[44,2578,2579],{},[47,2580,116],{},[44,2582,119],{},[29,2584,2585,2589],{},[44,2586,2587],{},[47,2588,126],{},[44,2590,129],{},[18,2592,49],{"id":132},[14,2594,135,2595,143],{},[137,2596,142],{"href":139,"rel":2597},[141],[145,2599,2600,2602,2606],{},[148,2601,150],{},[148,2603,153,2604],{},[53,2605,156],{},[148,2607,159,2608],{},[53,2609,162],{},[164,2611,167],{"id":166},[169,2613,2614],{"className":171,"code":172,"language":173,"meta":174,"style":174},[53,2615,2616,2620,2642,2664,2674,2678],{"__ignoreMap":174},[178,2617,2618],{"class":180,"line":181},[178,2619,185],{"class":184},[178,2621,2622,2624,2626,2628,2630,2632,2634,2636,2638,2640],{"class":180,"line":188},[178,2623,192],{"class":191},[178,2625,195],{"class":191},[178,2627,199],{"class":198},[178,2629,203],{"class":202},[178,2631,206],{"class":191},[178,2633,209],{"class":202},[178,2635,213],{"class":212},[178,2637,216],{"class":202},[178,2639,219],{"class":191},[178,2641,222],{"class":202},[178,2643,2644,2646,2648,2650,2652,2654,2656,2658,2660,2662],{"class":180,"line":225},[178,2645,228],{"class":191},[178,2647,231],{"class":202},[178,2649,235],{"class":234},[178,2651,238],{"class":202},[178,2653,241],{"class":234},[178,2655,244],{"class":202},[178,2657,247],{"class":191},[178,2659,250],{"class":191},[178,2661,253],{"class":198},[178,2663,256],{"class":202},[178,2665,2666,2668,2670,2672],{"class":180,"line":259},[178,2667,228],{"class":191},[178,2669,264],{"class":234},[178,2671,267],{"class":191},[178,2673,270],{"class":202},[178,2675,2676],{"class":180,"line":273},[178,2677,276],{"class":184},[178,2679,2680],{"class":180,"line":279},[178,2681,282],{"class":202},[169,2683,2684],{"className":171,"code":285,"language":173,"meta":174,"style":174},[53,2685,2686,2690,2694,2712,2716],{"__ignoreMap":174},[178,2687,2688],{"class":180,"line":181},[178,2689,292],{"class":184},[178,2691,2692],{"class":180,"line":188},[178,2693,297],{"class":184},[178,2695,2696,2698,2700,2702,2704,2706,2708,2710],{"class":180,"line":225},[178,2697,192],{"class":191},[178,2699,195],{"class":191},[178,2701,306],{"class":198},[178,2703,309],{"class":202},[178,2705,312],{"class":212},[178,2707,216],{"class":202},[178,2709,219],{"class":191},[178,2711,222],{"class":202},[178,2713,2714],{"class":180,"line":259},[178,2715,323],{"class":184},[178,2717,2718],{"class":180,"line":273},[178,2719,282],{"class":202},[18,2721,331],{"id":330},[14,2723,2724,337,2726,341],{},[47,2725,336],{},[47,2727,340],{},[169,2729,2731],{"className":2730,"code":346,"language":347},[345],[53,2732,346],{"__ignoreMap":174},[14,2734,352],{},[145,2736,2737,2739,2741],{},[148,2738,357],{},[148,2740,360],{},[148,2742,363],{},[18,2744,367],{"id":366},[14,2746,370],{},[169,2748,2749],{"className":171,"code":373,"language":173,"meta":174,"style":174},[53,2750,2751,2755,2769,2773,2777],{"__ignoreMap":174},[178,2752,2753],{"class":180,"line":181},[178,2754,380],{"class":184},[178,2756,2757,2759,2761,2763,2765,2767],{"class":180,"line":188},[178,2758,385],{"class":191},[178,2760,388],{"class":234},[178,2762,267],{"class":191},[178,2764,250],{"class":191},[178,2766,395],{"class":198},[178,2768,398],{"class":202},[178,2770,2771],{"class":180,"line":225},[178,2772,404],{"emptyLinePlaceholder":403},[178,2774,2775],{"class":180,"line":259},[178,2776,409],{"class":184},[178,2778,2779,2781,2783,2785,2787,2789],{"class":180,"line":273},[178,2780,385],{"class":191},[178,2782,416],{"class":234},[178,2784,267],{"class":191},[178,2786,250],{"class":191},[178,2788,423],{"class":198},[178,2790,426],{"class":202},[14,2792,429,2793,433],{},[53,2794,432],{},[18,2796,437],{"id":436},[23,2798,2799,2807],{},[26,2800,2801],{},[29,2802,2803,2805],{},[32,2804,446],{},[32,2806,449],{},[39,2808,2809,2817,2825,2835,2845,2855,2863,2873],{},[29,2810,2811,2815],{},[44,2812,2813],{},[47,2814,458],{},[44,2816,461],{},[29,2818,2819,2823],{},[44,2820,2821],{},[47,2822,468],{},[44,2824,471],{},[29,2826,2827,2831],{},[44,2828,2829],{},[47,2830,478],{},[44,2832,481,2833,485],{},[53,2834,484],{},[29,2836,2837,2841],{},[44,2838,2839],{},[47,2840,492],{},[44,2842,2843,498],{},[53,2844,497],{},[29,2846,2847,2851],{},[44,2848,2849],{},[47,2850,505],{},[44,2852,2853,511],{},[53,2854,510],{},[29,2856,2857,2861],{},[44,2858,2859],{},[47,2860,518],{},[44,2862,521],{},[29,2864,2865,2869],{},[44,2866,2867],{},[47,2868,528],{},[44,2870,531,2871,535],{},[53,2872,534],{},[29,2874,2875,2879],{},[44,2876,2877],{},[47,2878,542],{},[44,2880,2881,548],{},[53,2882,547],{},[18,2884,552],{"id":551},[14,2886,555],{},[145,2888,2889,2895,2899,2903],{},[148,2890,2891,562,2893],{},[47,2892,449],{},[53,2894,565],{},[148,2896,2897,571],{},[47,2898,570],{},[148,2900,2901,577],{},[47,2902,576],{},[148,2904,2905,583],{},[47,2906,582],{},[18,2908,587],{"id":586},[14,2910,590],{},[169,2912,2913],{"className":171,"code":593,"language":173,"meta":174,"style":174},[53,2914,2915,2919,2923,2933,2943,2953,2963,2973],{"__ignoreMap":174},[178,2916,2917],{"class":180,"line":181},[178,2918,600],{"class":184},[178,2920,2921],{"class":180,"line":188},[178,2922,605],{"class":202},[178,2924,2925,2927,2929,2931],{"class":180,"line":225},[178,2926,611],{"class":610},[178,2928,562],{"class":202},[178,2930,616],{"class":610},[178,2932,619],{"class":202},[178,2934,2935,2937,2939,2941],{"class":180,"line":259},[178,2936,624],{"class":610},[178,2938,562],{"class":202},[178,2940,629],{"class":610},[178,2942,619],{"class":202},[178,2944,2945,2947,2949,2951],{"class":180,"line":273},[178,2946,636],{"class":610},[178,2948,562],{"class":202},[178,2950,641],{"class":610},[178,2952,619],{"class":202},[178,2954,2955,2957,2959,2961],{"class":180,"line":279},[178,2956,648],{"class":610},[178,2958,562],{"class":202},[178,2960,653],{"class":610},[178,2962,619],{"class":202},[178,2964,2965,2967,2969,2971],{"class":180,"line":658},[178,2966,661],{"class":610},[178,2968,562],{"class":202},[178,2970,666],{"class":610},[178,2972,619],{"class":202},[178,2974,2975],{"class":180,"line":671},[178,2976,674],{"class":202},[14,2978,677,2979,681],{},[53,2980,680],{},[18,2982,685],{"id":684},[14,2984,688],{},[145,2986,2987,2991],{},[148,2988,693,2989],{},[53,2990,696],{},[148,2992,699,2993,703,2995],{},[53,2994,702],{},[53,2996,706],{},[18,2998,710],{"id":709},[712,3000,3001,3005,3011,3017,3025],{},[148,3002,3003,719],{},[47,3004,718],{},[148,3006,3007,725,3009,729],{},[47,3008,724],{},[53,3010,728],{},[148,3012,3013,735,3015,739],{},[47,3014,734],{},[53,3016,738],{},[148,3018,3019,745,3021,749,3023,753],{},[47,3020,744],{},[53,3022,748],{},[53,3024,752],{},[148,3026,3027,759],{},[47,3028,758],{},[18,3030,763],{"id":762},[145,3032,3033,3037,3041],{},[148,3034,3035,772],{},[137,3036,771],{"href":770},[148,3038,3039,779],{},[137,3040,778],{"href":777},[148,3042,3043,786],{},[137,3044,785],{"href":784},[788,3046,790],{},{"title":174,"searchDepth":188,"depth":188,"links":3048},[3049,3050,3053,3054,3055,3056,3057,3058,3059,3060],{"id":20,"depth":188,"text":21},{"id":132,"depth":188,"text":49,"children":3051},[3052],{"id":166,"depth":225,"text":167},{"id":330,"depth":188,"text":331},{"id":366,"depth":188,"text":367},{"id":436,"depth":188,"text":437},{"id":551,"depth":188,"text":552},{"id":586,"depth":188,"text":587},{"id":684,"depth":188,"text":685},{"id":709,"depth":188,"text":710},{"id":762,"depth":188,"text":763},{},{"title":5,"description":805},{"id":3064,"title":3065,"body":3066,"description":4151,"extension":806,"icon":4152,"meta":4153,"navigation":403,"path":4154,"section":4155,"seo":4156,"stem":4157,"__hash__":4158},"docs\u002Fdocs\u002F5.contributing\u002F2.coding-conventions.md","Coding Conventions",{"type":7,"value":3067,"toc":4129},[3068,3071,3074,3078,3086,3108,3112,3116,3417,3421,3499,3503,3507,3513,3517,3675,3679,3732,3736,3740,3821,3825,3932,3936,3940,3961,3965,3968,3997,4000,4043,4046,4105,4107,4126],[10,3069,3065],{"id":3070},"coding-conventions",[14,3072,3073],{},"This guide covers the coding standards and patterns used throughout Reqcore. Following these conventions ensures consistency and prevents common mistakes.",[18,3075,3077],{"id":3076},"typescript","TypeScript",[14,3079,3080,3081,3083,3084,2096],{},"Reqcore is fully typed. Nuxt 4 provides separate TypeScript contexts for ",[53,3082,1935],{}," and ",[53,3085,2103],{},[145,3087,3088,3097,3103],{},[148,3089,3090,3091,3093,3094,3096],{},"Server utilities are ",[47,3092,2092],{}," available in ",[53,3095,1935],{}," code (and vice versa)",[148,3098,3099,3100,3102],{},"Use ",[53,3101,2076],{}," for types and functions needed in both contexts",[148,3104,3105,3106],{},"API response types are inferred automatically by ",[53,3107,2357],{},[18,3109,3111],{"id":3110},"vue-components","Vue Components",[164,3113,3115],{"id":3114},"file-organization","File Organization",[169,3117,3121],{"className":3118,"code":3119,"language":3120,"meta":174,"style":174},"language-vue shiki shiki-themes github-light github-dark","\u003Cscript setup lang=\"ts\">\n\u002F\u002F 1. Imports (framework, libraries, types)\nimport { Briefcase, ArrowRight } from 'lucide-vue-next'\n\n\u002F\u002F 2. Page meta & SEO\ndefinePageMeta({ layout: 'dashboard', middleware: ['auth'] })\nuseSeoMeta({ title: 'Jobs', description: '...' })\n\n\u002F\u002F 3. Reactive state & composables\nconst { data: jobs } = await useFetch('\u002Fapi\u002Fjobs', {\n  headers: useRequestHeaders(['cookie']),\n})\n\n\u002F\u002F 4. Computed properties\nconst openJobs = computed(() =>\n  jobs.value?.filter(j => j.status === 'open') ?? []\n)\n\n\u002F\u002F 5. Methods\nfunction handleCreate() { \u002F* ... *\u002F }\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003C!-- Component template -->\n\u003C\u002Ftemplate>\n","vue",[53,3122,3123,3146,3151,3164,3168,3173,3193,3213,3217,3223,3255,3273,3278,3283,3289,3308,3342,3348,3353,3359,3377,3387,3392,3402,3408],{"__ignoreMap":174},[178,3124,3125,3128,3132,3135,3138,3140,3143],{"class":180,"line":181},[178,3126,3127],{"class":202},"\u003C",[178,3129,3131],{"class":3130},"s9eBZ","script",[178,3133,3134],{"class":198}," setup",[178,3136,3137],{"class":198}," lang",[178,3139,247],{"class":202},[178,3141,3142],{"class":610},"\"ts\"",[178,3144,3145],{"class":202},">\n",[178,3147,3148],{"class":180,"line":188},[178,3149,3150],{"class":184},"\u002F\u002F 1. Imports (framework, libraries, types)\n",[178,3152,3153,3156,3159,3161],{"class":180,"line":225},[178,3154,3155],{"class":191},"import",[178,3157,3158],{"class":202}," { Briefcase, ArrowRight } ",[178,3160,1797],{"class":191},[178,3162,3163],{"class":610}," 'lucide-vue-next'\n",[178,3165,3166],{"class":180,"line":259},[178,3167,404],{"emptyLinePlaceholder":403},[178,3169,3170],{"class":180,"line":273},[178,3171,3172],{"class":184},"\u002F\u002F 2. Page meta & SEO\n",[178,3174,3175,3178,3181,3184,3187,3190],{"class":180,"line":279},[178,3176,3177],{"class":198},"definePageMeta",[178,3179,3180],{"class":202},"({ layout: ",[178,3182,3183],{"class":610},"'dashboard'",[178,3185,3186],{"class":202},", middleware: [",[178,3188,3189],{"class":610},"'auth'",[178,3191,3192],{"class":202},"] })\n",[178,3194,3195,3198,3201,3204,3207,3210],{"class":180,"line":658},[178,3196,3197],{"class":198},"useSeoMeta",[178,3199,3200],{"class":202},"({ title: ",[178,3202,3203],{"class":610},"'Jobs'",[178,3205,3206],{"class":202},", description: ",[178,3208,3209],{"class":610},"'...'",[178,3211,3212],{"class":202}," })\n",[178,3214,3215],{"class":180,"line":671},[178,3216,404],{"emptyLinePlaceholder":403},[178,3218,3220],{"class":180,"line":3219},9,[178,3221,3222],{"class":184},"\u002F\u002F 3. Reactive state & composables\n",[178,3224,3226,3228,3230,3233,3235,3238,3240,3242,3244,3247,3249,3252],{"class":180,"line":3225},10,[178,3227,385],{"class":191},[178,3229,231],{"class":202},[178,3231,3232],{"class":212},"data",[178,3234,562],{"class":202},[178,3236,3237],{"class":234},"jobs",[178,3239,244],{"class":202},[178,3241,247],{"class":191},[178,3243,250],{"class":191},[178,3245,3246],{"class":198}," useFetch",[178,3248,203],{"class":202},[178,3250,3251],{"class":610},"'\u002Fapi\u002Fjobs'",[178,3253,3254],{"class":202},", {\n",[178,3256,3258,3261,3264,3267,3270],{"class":180,"line":3257},11,[178,3259,3260],{"class":202},"  headers: ",[178,3262,3263],{"class":198},"useRequestHeaders",[178,3265,3266],{"class":202},"([",[178,3268,3269],{"class":610},"'cookie'",[178,3271,3272],{"class":202},"]),\n",[178,3274,3276],{"class":180,"line":3275},12,[178,3277,282],{"class":202},[178,3279,3281],{"class":180,"line":3280},13,[178,3282,404],{"emptyLinePlaceholder":403},[178,3284,3286],{"class":180,"line":3285},14,[178,3287,3288],{"class":184},"\u002F\u002F 4. Computed properties\n",[178,3290,3292,3294,3297,3299,3302,3305],{"class":180,"line":3291},15,[178,3293,385],{"class":191},[178,3295,3296],{"class":234}," openJobs",[178,3298,267],{"class":191},[178,3300,3301],{"class":198}," computed",[178,3303,3304],{"class":202},"(() ",[178,3306,3307],{"class":191},"=>\n",[178,3309,3311,3314,3317,3319,3322,3325,3328,3331,3334,3336,3339],{"class":180,"line":3310},16,[178,3312,3313],{"class":202},"  jobs.value?.",[178,3315,3316],{"class":198},"filter",[178,3318,203],{"class":202},[178,3320,3321],{"class":212},"j",[178,3323,3324],{"class":191}," =>",[178,3326,3327],{"class":202}," j.status ",[178,3329,3330],{"class":191},"===",[178,3332,3333],{"class":610}," 'open'",[178,3335,216],{"class":202},[178,3337,3338],{"class":191},"??",[178,3340,3341],{"class":202}," []\n",[178,3343,3345],{"class":180,"line":3344},17,[178,3346,3347],{"class":202},")\n",[178,3349,3351],{"class":180,"line":3350},18,[178,3352,404],{"emptyLinePlaceholder":403},[178,3354,3356],{"class":180,"line":3355},19,[178,3357,3358],{"class":184},"\u002F\u002F 5. Methods\n",[178,3360,3362,3365,3368,3371,3374],{"class":180,"line":3361},20,[178,3363,3364],{"class":191},"function",[178,3366,3367],{"class":198}," handleCreate",[178,3369,3370],{"class":202},"() { ",[178,3372,3373],{"class":184},"\u002F* ... *\u002F",[178,3375,3376],{"class":202}," }\n",[178,3378,3380,3383,3385],{"class":180,"line":3379},21,[178,3381,3382],{"class":202},"\u003C\u002F",[178,3384,3131],{"class":3130},[178,3386,3145],{"class":202},[178,3388,3390],{"class":180,"line":3389},22,[178,3391,404],{"emptyLinePlaceholder":403},[178,3393,3395,3397,3400],{"class":180,"line":3394},23,[178,3396,3127],{"class":202},[178,3398,3399],{"class":3130},"template",[178,3401,3145],{"class":202},[178,3403,3405],{"class":180,"line":3404},24,[178,3406,3407],{"class":184},"  \u003C!-- Component template -->\n",[178,3409,3411,3413,3415],{"class":180,"line":3410},25,[178,3412,3382],{"class":202},[178,3414,3399],{"class":3130},[178,3416,3145],{"class":202},[164,3418,3420],{"id":3419},"naming","Naming",[23,3422,3423,3436],{},[26,3424,3425],{},[29,3426,3427,3430,3433],{},[32,3428,3429],{},"Item",[32,3431,3432],{},"Convention",[32,3434,3435],{},"Example",[39,3437,3438,3454,3474,3487],{},[29,3439,3440,3443,3446],{},[44,3441,3442],{},"Components",[44,3444,3445],{},"PascalCase",[44,3447,3448,238,3451],{},[53,3449,3450],{},"PipelineCard.vue",[53,3452,3453],{},"OrgSwitcher.vue",[29,3455,3456,3459,3466],{},[44,3457,3458],{},"Composables",[44,3460,3461,3462,3465],{},"camelCase with ",[53,3463,3464],{},"use"," prefix",[44,3467,3468,238,3471],{},[53,3469,3470],{},"useJobs()",[53,3472,3473],{},"useCandidate()",[29,3475,3476,3479,3482],{},[44,3477,3478],{},"Pages",[44,3480,3481],{},"kebab-case",[44,3483,3484],{},[53,3485,3486],{},"application-form.vue",[29,3488,3489,3492,3494],{},[44,3490,3491],{},"Layouts",[44,3493,3481],{},[44,3495,3496],{},[53,3497,3498],{},"dashboard.vue",[18,3500,3502],{"id":3501},"api-routes","API Routes",[164,3504,3506],{"id":3505},"file-naming","File Naming",[169,3508,3511],{"className":3509,"code":3510,"language":347},[345],"server\u002Fapi\u002Fjobs\u002Findex.get.ts     → GET \u002Fapi\u002Fjobs\nserver\u002Fapi\u002Fjobs\u002Findex.post.ts    → POST \u002Fapi\u002Fjobs\nserver\u002Fapi\u002Fjobs\u002F[id].get.ts      → GET \u002Fapi\u002Fjobs\u002F:id\nserver\u002Fapi\u002Fjobs\u002F[id].patch.ts    → PATCH \u002Fapi\u002Fjobs\u002F:id\nserver\u002Fapi\u002Fjobs\u002F[id].delete.ts   → DELETE \u002Fapi\u002Fjobs\u002F:id\n",[53,3512,3510],{"__ignoreMap":174},[164,3514,3516],{"id":3515},"handler-pattern","Handler Pattern",[169,3518,3520],{"className":171,"code":3519,"language":173,"meta":174,"style":174},"export default defineEventHandler(async (event) => {\n  \u002F\u002F 1. Authenticate\n  const { session } = await requireAuth(event)\n  const orgId = session.activeOrganizationId\n\n  \u002F\u002F 2. Validate input\n  const body = await readValidatedBody(event, schema.parse)\n\n  \u002F\u002F 3. Query (always scoped by orgId)\n  const result = await db\n    .select()\n    .from(table)\n    .where(eq(table.organizationId, orgId))\n\n  \u002F\u002F 4. Return data\n  return result\n})\n",[53,3521,3522,3544,3549,3567,3577,3581,3586,3601,3605,3610,3623,3632,3641,3654,3658,3663,3671],{"__ignoreMap":174},[178,3523,3524,3526,3528,3530,3532,3534,3536,3538,3540,3542],{"class":180,"line":181},[178,3525,192],{"class":191},[178,3527,195],{"class":191},[178,3529,199],{"class":198},[178,3531,203],{"class":202},[178,3533,206],{"class":191},[178,3535,209],{"class":202},[178,3537,213],{"class":212},[178,3539,216],{"class":202},[178,3541,219],{"class":191},[178,3543,222],{"class":202},[178,3545,3546],{"class":180,"line":188},[178,3547,3548],{"class":184},"  \u002F\u002F 1. Authenticate\n",[178,3550,3551,3553,3555,3557,3559,3561,3563,3565],{"class":180,"line":225},[178,3552,228],{"class":191},[178,3554,231],{"class":202},[178,3556,235],{"class":234},[178,3558,244],{"class":202},[178,3560,247],{"class":191},[178,3562,250],{"class":191},[178,3564,253],{"class":198},[178,3566,256],{"class":202},[178,3568,3569,3571,3573,3575],{"class":180,"line":259},[178,3570,228],{"class":191},[178,3572,264],{"class":234},[178,3574,267],{"class":191},[178,3576,270],{"class":202},[178,3578,3579],{"class":180,"line":273},[178,3580,404],{"emptyLinePlaceholder":403},[178,3582,3583],{"class":180,"line":279},[178,3584,3585],{"class":184},"  \u002F\u002F 2. Validate input\n",[178,3587,3588,3590,3592,3594,3596,3598],{"class":180,"line":658},[178,3589,228],{"class":191},[178,3591,388],{"class":234},[178,3593,267],{"class":191},[178,3595,250],{"class":191},[178,3597,395],{"class":198},[178,3599,3600],{"class":202},"(event, schema.parse)\n",[178,3602,3603],{"class":180,"line":671},[178,3604,404],{"emptyLinePlaceholder":403},[178,3606,3607],{"class":180,"line":3219},[178,3608,3609],{"class":184},"  \u002F\u002F 3. Query (always scoped by orgId)\n",[178,3611,3612,3614,3617,3619,3621],{"class":180,"line":3225},[178,3613,228],{"class":191},[178,3615,3616],{"class":234}," result",[178,3618,267],{"class":191},[178,3620,250],{"class":191},[178,3622,1779],{"class":202},[178,3624,3625,3628,3630],{"class":180,"line":3257},[178,3626,3627],{"class":202},"    .",[178,3629,1787],{"class":198},[178,3631,1790],{"class":202},[178,3633,3634,3636,3638],{"class":180,"line":3275},[178,3635,3627],{"class":202},[178,3637,1797],{"class":198},[178,3639,3640],{"class":202},"(table)\n",[178,3642,3643,3645,3647,3649,3651],{"class":180,"line":3280},[178,3644,3627],{"class":202},[178,3646,1807],{"class":198},[178,3648,203],{"class":202},[178,3650,1812],{"class":198},[178,3652,3653],{"class":202},"(table.organizationId, orgId))\n",[178,3655,3656],{"class":180,"line":3285},[178,3657,404],{"emptyLinePlaceholder":403},[178,3659,3660],{"class":180,"line":3291},[178,3661,3662],{"class":184},"  \u002F\u002F 4. Return data\n",[178,3664,3665,3668],{"class":180,"line":3310},[178,3666,3667],{"class":191},"  return",[178,3669,3670],{"class":202}," result\n",[178,3672,3673],{"class":180,"line":3344},[178,3674,282],{"class":202},[164,3676,3678],{"id":3677},"error-handling","Error Handling",[169,3680,3682],{"className":171,"code":3681,"language":173,"meta":174,"style":174},"\u002F\u002F ✅ Correct — throws proper HTTP error\nthrow createError({ statusCode: 404, message: 'Job not found' })\n\n\u002F\u002F ❌ Wrong — returns 200 with error body\nreturn { error: 'Job not found' }\n",[53,3683,3684,3689,3711,3715,3720],{"__ignoreMap":174},[178,3685,3686],{"class":180,"line":181},[178,3687,3688],{"class":184},"\u002F\u002F ✅ Correct — throws proper HTTP error\n",[178,3690,3691,3694,3697,3700,3703,3706,3709],{"class":180,"line":188},[178,3692,3693],{"class":191},"throw",[178,3695,3696],{"class":198}," createError",[178,3698,3699],{"class":202},"({ statusCode: ",[178,3701,3702],{"class":234},"404",[178,3704,3705],{"class":202},", message: ",[178,3707,3708],{"class":610},"'Job not found'",[178,3710,3212],{"class":202},[178,3712,3713],{"class":180,"line":225},[178,3714,404],{"emptyLinePlaceholder":403},[178,3716,3717],{"class":180,"line":259},[178,3718,3719],{"class":184},"\u002F\u002F ❌ Wrong — returns 200 with error body\n",[178,3721,3722,3725,3728,3730],{"class":180,"line":273},[178,3723,3724],{"class":191},"return",[178,3726,3727],{"class":202}," { error: ",[178,3729,3708],{"class":610},[178,3731,3376],{"class":202},[18,3733,3735],{"id":3734},"database-queries","Database Queries",[164,3737,3739],{"id":3738},"always-scope-by-organization","Always Scope by Organization",[169,3741,3743],{"className":171,"code":3742,"language":173,"meta":174,"style":174},"\u002F\u002F ✅ Every query MUST include organizationId\nconst jobs = await db\n  .select()\n  .from(job)\n  .where(eq(job.organizationId, orgId))\n\n\u002F\u002F ❌ Never query without org scope\nconst jobs = await db.select().from(job)\n",[53,3744,3745,3750,3762,3770,3778,3790,3794,3799],{"__ignoreMap":174},[178,3746,3747],{"class":180,"line":181},[178,3748,3749],{"class":184},"\u002F\u002F ✅ Every query MUST include organizationId\n",[178,3751,3752,3754,3756,3758,3760],{"class":180,"line":188},[178,3753,385],{"class":191},[178,3755,1772],{"class":234},[178,3757,267],{"class":191},[178,3759,250],{"class":191},[178,3761,1779],{"class":202},[178,3763,3764,3766,3768],{"class":180,"line":225},[178,3765,1784],{"class":202},[178,3767,1787],{"class":198},[178,3769,1790],{"class":202},[178,3771,3772,3774,3776],{"class":180,"line":259},[178,3773,1784],{"class":202},[178,3775,1797],{"class":198},[178,3777,1800],{"class":202},[178,3779,3780,3782,3784,3786,3788],{"class":180,"line":273},[178,3781,1784],{"class":202},[178,3783,1807],{"class":198},[178,3785,203],{"class":202},[178,3787,1812],{"class":198},[178,3789,1815],{"class":202},[178,3791,3792],{"class":180,"line":279},[178,3793,404],{"emptyLinePlaceholder":403},[178,3795,3796],{"class":180,"line":658},[178,3797,3798],{"class":184},"\u002F\u002F ❌ Never query without org scope\n",[178,3800,3801,3803,3805,3807,3809,3812,3814,3817,3819],{"class":180,"line":671},[178,3802,385],{"class":191},[178,3804,1772],{"class":234},[178,3806,267],{"class":191},[178,3808,250],{"class":191},[178,3810,3811],{"class":202}," db.",[178,3813,1787],{"class":198},[178,3815,3816],{"class":202},"().",[178,3818,1797],{"class":198},[178,3820,1800],{"class":202},[164,3822,3824],{"id":3823},"use-drizzle-operators","Use Drizzle Operators",[169,3826,3828],{"className":171,"code":3827,"language":173,"meta":174,"style":174},"import { eq, and, desc } from 'drizzle-orm'\n\nconst results = await db\n  .select()\n  .from(application)\n  .where(\n    and(\n      eq(application.organizationId, orgId),\n      eq(application.jobId, jobId),\n    )\n  )\n  .orderBy(desc(application.createdAt))\n",[53,3829,3830,3842,3846,3859,3867,3876,3885,3892,3900,3907,3912,3917],{"__ignoreMap":174},[178,3831,3832,3834,3837,3839],{"class":180,"line":181},[178,3833,3155],{"class":191},[178,3835,3836],{"class":202}," { eq, and, desc } ",[178,3838,1797],{"class":191},[178,3840,3841],{"class":610}," 'drizzle-orm'\n",[178,3843,3844],{"class":180,"line":188},[178,3845,404],{"emptyLinePlaceholder":403},[178,3847,3848,3850,3853,3855,3857],{"class":180,"line":225},[178,3849,385],{"class":191},[178,3851,3852],{"class":234}," results",[178,3854,267],{"class":191},[178,3856,250],{"class":191},[178,3858,1779],{"class":202},[178,3860,3861,3863,3865],{"class":180,"line":259},[178,3862,1784],{"class":202},[178,3864,1787],{"class":198},[178,3866,1790],{"class":202},[178,3868,3869,3871,3873],{"class":180,"line":273},[178,3870,1784],{"class":202},[178,3872,1797],{"class":198},[178,3874,3875],{"class":202},"(application)\n",[178,3877,3878,3880,3882],{"class":180,"line":279},[178,3879,1784],{"class":202},[178,3881,1807],{"class":198},[178,3883,3884],{"class":202},"(\n",[178,3886,3887,3890],{"class":180,"line":658},[178,3888,3889],{"class":198},"    and",[178,3891,3884],{"class":202},[178,3893,3894,3897],{"class":180,"line":671},[178,3895,3896],{"class":198},"      eq",[178,3898,3899],{"class":202},"(application.organizationId, orgId),\n",[178,3901,3902,3904],{"class":180,"line":3219},[178,3903,3896],{"class":198},[178,3905,3906],{"class":202},"(application.jobId, jobId),\n",[178,3908,3909],{"class":180,"line":3225},[178,3910,3911],{"class":202},"    )\n",[178,3913,3914],{"class":180,"line":3257},[178,3915,3916],{"class":202},"  )\n",[178,3918,3919,3921,3924,3926,3929],{"class":180,"line":3275},[178,3920,1784],{"class":202},[178,3922,3923],{"class":198},"orderBy",[178,3925,203],{"class":202},[178,3927,3928],{"class":198},"desc",[178,3930,3931],{"class":202},"(application.createdAt))\n",[18,3933,3935],{"id":3934},"css-styling","CSS & Styling",[164,3937,3939],{"id":3938},"tailwind-css-v4","Tailwind CSS v4",[145,3941,3942,3945,3955],{},[148,3943,3944],{},"Use utility classes — avoid custom CSS",[148,3946,3947,3948,3951,3952],{},"Theme tokens are defined in ",[53,3949,3950],{},"app\u002Fassets\u002Fcss\u002Fmain.css"," via ",[53,3953,3954],{},"@theme",[148,3956,1954,3957,3960],{},[53,3958,3959],{},"brand-*"," color scale maps to the primary color",[164,3962,3964],{"id":3963},"dark-theme-pages","Dark Theme Pages",[14,3966,3967],{},"Public pages (landing, blog, roadmap, docs) use a dark theme:",[169,3969,3971],{"className":3118,"code":3970,"language":3120,"meta":174,"style":174},"useHead({\n  bodyAttrs: {\n    style: 'background-color: #09090b;',\n  },\n})\n",[53,3972,3973,3978,3983,3988,3993],{"__ignoreMap":174},[178,3974,3975],{"class":180,"line":181},[178,3976,3977],{"class":202},"useHead({\n",[178,3979,3980],{"class":180,"line":188},[178,3981,3982],{"class":202},"  bodyAttrs: {\n",[178,3984,3985],{"class":180,"line":225},[178,3986,3987],{"class":202},"    style: 'background-color: #09090b;',\n",[178,3989,3990],{"class":180,"line":259},[178,3991,3992],{"class":202},"  },\n",[178,3994,3995],{"class":180,"line":273},[178,3996,282],{"class":202},[14,3998,3999],{},"Common dark theme patterns:",[145,4001,4002,4008,4020,4026,4036],{},[148,4003,4004,4005],{},"Background: ",[53,4006,4007],{},"bg-[#09090b]",[148,4009,4010,4011,238,4014,238,4017],{},"Text: ",[53,4012,4013],{},"text-white",[53,4015,4016],{},"text-white\u002F50",[53,4018,4019],{},"text-white\u002F40",[148,4021,4022,4023],{},"Borders: ",[53,4024,4025],{},"border-white\u002F[0.06]",[148,4027,4028,4029,4032,4033],{},"Cards: ",[53,4030,4031],{},"bg-white\u002F[0.02]",", hover: ",[53,4034,4035],{},"bg-white\u002F[0.04]",[148,4037,4038,4039,4042],{},"Glow: ",[53,4040,4041],{},"blur-[120px]"," with brand colors",[18,4044,785],{"id":4045},"environment-variables",[169,4047,4049],{"className":171,"code":4048,"language":173,"meta":174,"style":174},"\u002F\u002F ✅ Use the validated env utility\nimport { env } from '~\u002Fserver\u002Futils\u002Fenv'\nconst dbUrl = env.DATABASE_URL\n\n\u002F\u002F ❌ Never access process.env directly in server code\nconst dbUrl = process.env.DATABASE_URL\n",[53,4050,4051,4056,4068,4083,4087,4092],{"__ignoreMap":174},[178,4052,4053],{"class":180,"line":181},[178,4054,4055],{"class":184},"\u002F\u002F ✅ Use the validated env utility\n",[178,4057,4058,4060,4063,4065],{"class":180,"line":188},[178,4059,3155],{"class":191},[178,4061,4062],{"class":202}," { env } ",[178,4064,1797],{"class":191},[178,4066,4067],{"class":610}," '~\u002Fserver\u002Futils\u002Fenv'\n",[178,4069,4070,4072,4075,4077,4080],{"class":180,"line":225},[178,4071,385],{"class":191},[178,4073,4074],{"class":234}," dbUrl",[178,4076,267],{"class":191},[178,4078,4079],{"class":202}," env.",[178,4081,4082],{"class":234},"DATABASE_URL\n",[178,4084,4085],{"class":180,"line":259},[178,4086,404],{"emptyLinePlaceholder":403},[178,4088,4089],{"class":180,"line":273},[178,4090,4091],{"class":184},"\u002F\u002F ❌ Never access process.env directly in server code\n",[178,4093,4094,4096,4098,4100,4103],{"class":180,"line":279},[178,4095,385],{"class":191},[178,4097,4074],{"class":234},[178,4099,267],{"class":191},[178,4101,4102],{"class":202}," process.env.",[178,4104,4082],{"class":234},[18,4106,763],{"id":762},[145,4108,4109,4116,4121],{},[148,4110,4111,4115],{},[137,4112,4114],{"href":4113},"\u002Fdocs\u002Fcontributing\u002Fdevelopment-setup","Development Setup"," — Get your environment ready",[148,4117,4118,4120],{},[137,4119,771],{"href":770}," — Understand the codebase",[148,4122,4123,4125],{},[137,4124,5],{"href":809}," — Security invariants to never break",[788,4127,4128],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":174,"searchDepth":188,"depth":188,"links":4130},[4131,4132,4136,4141,4145,4149,4150],{"id":3076,"depth":188,"text":3077},{"id":3110,"depth":188,"text":3111,"children":4133},[4134,4135],{"id":3114,"depth":225,"text":3115},{"id":3419,"depth":225,"text":3420},{"id":3501,"depth":188,"text":3502,"children":4137},[4138,4139,4140],{"id":3505,"depth":225,"text":3506},{"id":3515,"depth":225,"text":3516},{"id":3677,"depth":225,"text":3678},{"id":3734,"depth":188,"text":3735,"children":4142},[4143,4144],{"id":3738,"depth":225,"text":3739},{"id":3823,"depth":225,"text":3824},{"id":3934,"depth":188,"text":3935,"children":4146},[4147,4148],{"id":3938,"depth":225,"text":3939},{"id":3963,"depth":225,"text":3964},{"id":4045,"depth":188,"text":785},{"id":762,"depth":188,"text":763},"Code style, naming patterns, and best practices for Reqcore development. Covers Vue components, API routes, Drizzle queries, and more.","file-code",{},"\u002Fdocs\u002Fcontributing\u002Fcoding-conventions","Contributing",{"title":3065,"description":4151},"docs\u002F5.contributing\u002F2.coding-conventions","lqQYmIOBCkDMfjCJcVJRCUC3OFlMjdjVs_G9W4aXt2c",{"id":4160,"title":4114,"body":4161,"description":4732,"extension":806,"icon":53,"meta":4733,"navigation":403,"path":4113,"section":4155,"seo":4734,"stem":4735,"__hash__":4736},"docs\u002Fdocs\u002F5.contributing\u002F1.development-setup.md",{"type":7,"value":4162,"toc":4716},[4163,4166,4169,4173,4201,4205,4321,4328,4332,4435,4439,4495,4499,4512,4514,4518,4550,4554,4576,4580,4613,4617,4648,4652,4692,4694,4713],[10,4164,4114],{"id":4165},"development-setup",[14,4167,4168],{},"This guide covers everything you need to contribute code to Reqcore.",[18,4170,4172],{"id":4171},"prerequisites","Prerequisites",[145,4174,4175,4183,4191,4198],{},[148,4176,4177,4182],{},[137,4178,4181],{"href":4179,"rel":4180},"https:\u002F\u002Fnodejs.org\u002F",[141],"Node.js"," 18.20+ (LTS 20+ recommended)",[148,4184,4185,4190],{},[137,4186,4189],{"href":4187,"rel":4188},"https:\u002F\u002Fwww.docker.com\u002F",[141],"Docker"," and Docker Compose",[148,4192,4193],{},[137,4194,4197],{"href":4195,"rel":4196},"https:\u002F\u002Fgit-scm.com\u002F",[141],"Git",[148,4199,4200],{},"A code editor (VS Code recommended)",[18,4202,4204],{"id":4203},"getting-started","Getting Started",[169,4206,4208],{"className":1825,"code":4207,"language":1827,"meta":174,"style":174},"# 1. Fork and clone the repository\ngit clone https:\u002F\u002Fgithub.com\u002FYOUR_USERNAME\u002Freqcore.git\ncd reqcore\n\n# 2. Copy environment file\ncp .env.example .env\n# Or run setup.sh to generate random secrets:\n.\u002Fsetup.sh\n\n# 3. Start infrastructure services\ndocker compose up -d\n\n# 4. Install dependencies\nnpm install\n\n# 5. Start the dev server\nnpm run dev\n",[53,4209,4210,4215,4226,4234,4238,4243,4254,4259,4264,4268,4273,4287,4291,4296,4303,4307,4312],{"__ignoreMap":174},[178,4211,4212],{"class":180,"line":181},[178,4213,4214],{"class":184},"# 1. Fork and clone the repository\n",[178,4216,4217,4220,4223],{"class":180,"line":188},[178,4218,4219],{"class":198},"git",[178,4221,4222],{"class":610}," clone",[178,4224,4225],{"class":610}," https:\u002F\u002Fgithub.com\u002FYOUR_USERNAME\u002Freqcore.git\n",[178,4227,4228,4231],{"class":180,"line":225},[178,4229,4230],{"class":234},"cd",[178,4232,4233],{"class":610}," reqcore\n",[178,4235,4236],{"class":180,"line":259},[178,4237,404],{"emptyLinePlaceholder":403},[178,4239,4240],{"class":180,"line":273},[178,4241,4242],{"class":184},"# 2. Copy environment file\n",[178,4244,4245,4248,4251],{"class":180,"line":279},[178,4246,4247],{"class":198},"cp",[178,4249,4250],{"class":610}," .env.example",[178,4252,4253],{"class":610}," .env\n",[178,4255,4256],{"class":180,"line":658},[178,4257,4258],{"class":184},"# Or run setup.sh to generate random secrets:\n",[178,4260,4261],{"class":180,"line":671},[178,4262,4263],{"class":198},".\u002Fsetup.sh\n",[178,4265,4266],{"class":180,"line":3219},[178,4267,404],{"emptyLinePlaceholder":403},[178,4269,4270],{"class":180,"line":3225},[178,4271,4272],{"class":184},"# 3. Start infrastructure services\n",[178,4274,4275,4278,4281,4284],{"class":180,"line":3257},[178,4276,4277],{"class":198},"docker",[178,4279,4280],{"class":610}," compose",[178,4282,4283],{"class":610}," up",[178,4285,4286],{"class":234}," -d\n",[178,4288,4289],{"class":180,"line":3275},[178,4290,404],{"emptyLinePlaceholder":403},[178,4292,4293],{"class":180,"line":3280},[178,4294,4295],{"class":184},"# 4. Install dependencies\n",[178,4297,4298,4300],{"class":180,"line":3285},[178,4299,1839],{"class":198},[178,4301,4302],{"class":610}," install\n",[178,4304,4305],{"class":180,"line":3291},[178,4306,404],{"emptyLinePlaceholder":403},[178,4308,4309],{"class":180,"line":3310},[178,4310,4311],{"class":184},"# 5. Start the dev server\n",[178,4313,4314,4316,4318],{"class":180,"line":3344},[178,4315,1839],{"class":198},[178,4317,1842],{"class":610},[178,4319,4320],{"class":610}," dev\n",[14,4322,4323,4324,835],{},"The app is available at ",[137,4325,4326],{"href":4326,"rel":4327},"http:\u002F\u002Flocalhost:3000",[141],[18,4329,4331],{"id":4330},"development-commands","Development Commands",[23,4333,4334,4343],{},[26,4335,4336],{},[29,4337,4338,4341],{},[32,4339,4340],{},"Command",[32,4342,2254],{},[39,4344,4345,4355,4365,4375,4385,4395,4405,4415,4425],{},[29,4346,4347,4352],{},[44,4348,4349],{},[53,4350,4351],{},"npm run dev",[44,4353,4354],{},"Start Nuxt development server",[29,4356,4357,4362],{},[44,4358,4359],{},[53,4360,4361],{},"npm run build",[44,4363,4364],{},"Build for production",[29,4366,4367,4372],{},[44,4368,4369],{},[53,4370,4371],{},"npm run preview",[44,4373,4374],{},"Preview production build",[29,4376,4377,4382],{},[44,4378,4379],{},[53,4380,4381],{},"npm run db:generate",[44,4383,4384],{},"Generate a migration after schema changes",[29,4386,4387,4392],{},[44,4388,4389],{},[53,4390,4391],{},"npm run db:migrate",[44,4393,4394],{},"Apply pending migrations",[29,4396,4397,4402],{},[44,4398,4399],{},[53,4400,4401],{},"npm run db:push",[44,4403,4404],{},"Push schema changes directly (dev only)",[29,4406,4407,4412],{},[44,4408,4409],{},[53,4410,4411],{},"npm run db:studio",[44,4413,4414],{},"Open Drizzle Studio (database GUI)",[29,4416,4417,4422],{},[44,4418,4419],{},[53,4420,4421],{},"npm run db:seed",[44,4423,4424],{},"Seed the database with sample data",[29,4426,4427,4432],{},[44,4428,4429],{},[53,4430,4431],{},"npm run test:e2e",[44,4433,4434],{},"Run Playwright end-to-end tests",[18,4436,4438],{"id":4437},"branch-and-commit-workflow","Branch and Commit Workflow",[712,4440,4441,4465,4468,4492],{},[148,4442,4443,4444,2096,4447],{},"Create a topic branch from ",[53,4445,4446],{},"main",[169,4448,4450],{"className":1825,"code":4449,"language":1827,"meta":174,"style":174},"git checkout -b feat\u002Fmy-feature\n",[53,4451,4452],{"__ignoreMap":174},[178,4453,4454,4456,4459,4462],{"class":180,"line":181},[178,4455,4219],{"class":198},[178,4457,4458],{"class":610}," checkout",[178,4460,4461],{"class":234}," -b",[178,4463,4464],{"class":610}," feat\u002Fmy-feature\n",[148,4466,4467],{},"Make your changes with focused, atomic commits",[148,4469,4470,4471],{},"Sign every commit with the DCO (Developer Certificate of Origin):",[169,4472,4474],{"className":1825,"code":4473,"language":1827,"meta":174,"style":174},"git commit -s -m \"feat: add candidate search\"\n",[53,4475,4476],{"__ignoreMap":174},[178,4477,4478,4480,4483,4486,4489],{"class":180,"line":181},[178,4479,4219],{"class":198},[178,4481,4482],{"class":610}," commit",[178,4484,4485],{"class":234}," -s",[178,4487,4488],{"class":234}," -m",[178,4490,4491],{"class":610}," \"feat: add candidate search\"\n",[148,4493,4494],{},"Push and open a pull request with a clear summary",[164,4496,4498],{"id":4497},"dco-sign-off","DCO Sign-Off",[14,4500,4501,4502,4507,4508,4511],{},"Reqcore uses the ",[137,4503,4506],{"href":4504,"rel":4505},"https:\u002F\u002Fdevelopercertificate.org\u002F",[141],"Developer Certificate of Origin"," instead of a CLA. Every commit must include a ",[53,4509,4510],{},"Signed-off-by:"," line. Pull requests fail CI if commits are missing sign-off.",[18,4513,3065],{"id":3070},[164,4515,4517],{"id":4516},"project-structure","Project Structure",[145,4519,4520,4529,4537,4542],{},[148,4521,4522,4523,4525,4526,4528],{},"Follow Nuxt 4 ",[53,4524,1935],{}," + root ",[53,4527,2103],{}," directory structure",[148,4530,4531,4532,4534,4535],{},"Client code goes in ",[53,4533,1935],{}," — never put components in ",[53,4536,2103],{},[148,4538,4539,4540],{},"Server code stays at the project root — never put API routes in ",[53,4541,1935],{},[148,4543,4544,4545,4547,4548],{},"Content files go in ",[53,4546,2109],{}," — not inside ",[53,4549,1935],{},[164,4551,4553],{"id":4552},"styling","Styling",[145,4555,4556,4561,4567],{},[148,4557,3099,4558,4560],{},[47,4559,3939],{}," utilities — no custom CSS unless absolutely necessary",[148,4562,3099,4563,4566],{},[47,4564,4565],{},"lucide-vue-next"," for icons (tree-shakeable, consistent)",[148,4568,4569,4570,4572,4573,4575],{},"Dark theme pages use ",[53,4571,4007],{}," with glass-like borders (",[53,4574,4025],{},")",[164,4577,4579],{"id":4578},"server-code","Server Code",[145,4581,4582,4594,4600,4606],{},[148,4583,4584,4585,4587,4588,4590,4591,4593],{},"Access environment variables through ",[53,4586,2283],{}," from ",[53,4589,2329],{}," — never use ",[53,4592,2333],{}," directly",[148,4595,4596,4597,4599],{},"Scope every database query by ",[53,4598,896],{}," from the session",[148,4601,3099,4602,4605],{},[53,4603,4604],{},"createError()"," for HTTP errors — never return error objects",[148,4607,4608,4609,93,4611],{},"Validate all input with Zod schemas via ",[53,4610,92],{},[53,4612,96],{},[164,4614,4616],{"id":4615},"client-code","Client Code",[145,4618,4619,4632,4637,4642],{},[148,4620,3099,4621,4623,4624,4627,4628,4631],{},[53,4622,2357],{}," or ",[53,4625,4626],{},"useAsyncData"," for data fetching — never ",[53,4629,4630],{},"$fetch"," in component setup",[148,4633,4634,4635],{},"Forward cookies during SSR: ",[53,4636,2361],{},[148,4638,3099,4639,4641],{},[53,4640,706],{}," for SEO meta tags",[148,4643,3099,4644,4647],{},[53,4645,4646],{},"definePageMeta()"," for page configuration (layout, middleware)",[18,4649,4651],{"id":4650},"pull-request-checklist","Pull Request Checklist",[145,4653,4656,4665,4671,4677,4683],{"className":4654},[4655],"contains-task-list",[148,4657,4660,4664],{"className":4658},[4659],"task-list-item",[4661,4662],"input",{"disabled":403,"type":4663},"checkbox"," Scoped changes to one concern",[148,4666,4668,4670],{"className":4667},[4659],[4661,4669],{"disabled":403,"type":4663}," Tested the change locally",[148,4672,4674,4676],{"className":4673},[4659],[4661,4675],{"disabled":403,"type":4663}," Updated documentation if behavior changed",[148,4678,4680,4682],{"className":4679},[4659],[4661,4681],{"disabled":403,"type":4663}," No tenant-scope or auth regressions",[148,4684,4686,4688,4689,4575],{"className":4685},[4659],[4661,4687],{"disabled":403,"type":4663}," All commits are DCO signed (",[53,4690,4691],{},"git commit -s",[18,4693,763],{"id":762},[145,4695,4696,4701,4706],{},[148,4697,4698,4700],{},[137,4699,3065],{"href":4154}," — Detailed style guide",[148,4702,4703,4705],{},[137,4704,771],{"href":770}," — Understanding the system",[148,4707,4708,4712],{},[137,4709,4711],{"href":4710},"\u002Fdocs\u002Fgetting-started\u002Fintroduction","Introduction"," — Product context",[788,4714,4715],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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":174,"searchDepth":188,"depth":188,"links":4717},[4718,4719,4720,4721,4724,4730,4731],{"id":4171,"depth":188,"text":4172},{"id":4203,"depth":188,"text":4204},{"id":4330,"depth":188,"text":4331},{"id":4437,"depth":188,"text":4438,"children":4722},[4723],{"id":4497,"depth":225,"text":4498},{"id":3070,"depth":188,"text":3065,"children":4725},[4726,4727,4728,4729],{"id":4516,"depth":225,"text":4517},{"id":4552,"depth":225,"text":4553},{"id":4578,"depth":225,"text":4579},{"id":4615,"depth":225,"text":4616},{"id":4650,"depth":188,"text":4651},{"id":762,"depth":188,"text":763},"Set up a local development environment for contributing to Reqcore. Covers tooling, code style, and workflow.",{},{"title":4114,"description":4732},"docs\u002F5.contributing\u002F1.development-setup","0jggVMwM-29pWyA-rYm-Eiym0ZEN2mhc_9M2bi3HytM",{"id":4738,"title":4739,"body":4740,"description":5196,"extension":806,"icon":5197,"meta":5198,"navigation":403,"path":5199,"section":5200,"seo":5201,"stem":5202,"__hash__":5203},"docs\u002Fdocs\u002F2.deployment\u002F1.docker-compose.md","Docker Compose",{"type":7,"value":4741,"toc":5183},[4742,4746,4753,4756,4762,4766,4846,4850,4853,4856,4893,4897,4900,4945,4951,4955,4958,4983,4987,5007,5011,5014,5062,5066,5165,5167,5181],[10,4743,4745],{"id":4744},"docker-compose-deployment","Docker Compose Deployment",[14,4747,4748,4749,4752],{},"Reqcore ships with a ",[53,4750,4751],{},"docker-compose.yml"," for local development and self-hosted production deployments. This gives you full control over your data — database, file storage, and application all run on your infrastructure.",[18,4754,810],{"id":4755},"architecture",[169,4757,4760],{"className":4758,"code":4759,"language":347},[345],"┌─────────────────────────────────────────┐\n│  Docker Compose                          │\n│                                          │\n│  ┌──────────────┐  ┌──────────────────┐ │\n│  │  PostgreSQL   │  │  MinIO (S3)      │ │\n│  │  Port 5432    │  │  Port 9000\u002F9001  │ │\n│  └──────┬───────┘  └───────┬──────────┘ │\n│         │                  │             │\n│  ┌──────┴──────────────────┴──────────┐ │\n│  │  Reqcore (Nuxt 4)                   │ │\n│  │  Port 3000                          │ │\n│  └─────────────────────────────────────┘ │\n│                                          │\n│  ┌──────────────┐                       │\n│  │  Adminer      │                      │\n│  │  Port 8080    │                      │\n│  └──────────────┘                       │\n└─────────────────────────────────────────┘\n",[53,4761,4759],{"__ignoreMap":174},[18,4763,4765],{"id":4764},"quick-start","Quick Start",[169,4767,4769],{"className":1825,"code":4768,"language":1827,"meta":174,"style":174},"# 1. Clone the repository\ngit clone https:\u002F\u002Fgithub.com\u002Freqcore-inc\u002Freqcore.git\ncd reqcore\n\n# 2. Generate secrets\n.\u002Fsetup.sh\n\n# 3. Start everything\ndocker compose up -d\n\n# 4. Install dependencies and start dev server\nnpm install\nnpm run dev\n",[53,4770,4771,4776,4785,4791,4795,4800,4804,4808,4813,4823,4827,4832,4838],{"__ignoreMap":174},[178,4772,4773],{"class":180,"line":181},[178,4774,4775],{"class":184},"# 1. Clone the repository\n",[178,4777,4778,4780,4782],{"class":180,"line":188},[178,4779,4219],{"class":198},[178,4781,4222],{"class":610},[178,4783,4784],{"class":610}," https:\u002F\u002Fgithub.com\u002Freqcore-inc\u002Freqcore.git\n",[178,4786,4787,4789],{"class":180,"line":225},[178,4788,4230],{"class":234},[178,4790,4233],{"class":610},[178,4792,4793],{"class":180,"line":259},[178,4794,404],{"emptyLinePlaceholder":403},[178,4796,4797],{"class":180,"line":273},[178,4798,4799],{"class":184},"# 2. Generate secrets\n",[178,4801,4802],{"class":180,"line":279},[178,4803,4263],{"class":198},[178,4805,4806],{"class":180,"line":658},[178,4807,404],{"emptyLinePlaceholder":403},[178,4809,4810],{"class":180,"line":671},[178,4811,4812],{"class":184},"# 3. Start everything\n",[178,4814,4815,4817,4819,4821],{"class":180,"line":3219},[178,4816,4277],{"class":198},[178,4818,4280],{"class":610},[178,4820,4283],{"class":610},[178,4822,4286],{"class":234},[178,4824,4825],{"class":180,"line":3225},[178,4826,404],{"emptyLinePlaceholder":403},[178,4828,4829],{"class":180,"line":3257},[178,4830,4831],{"class":184},"# 4. Install dependencies and start dev server\n",[178,4833,4834,4836],{"class":180,"line":3275},[178,4835,1839],{"class":198},[178,4837,4302],{"class":610},[178,4839,4840,4842,4844],{"class":180,"line":3280},[178,4841,1839],{"class":198},[178,4843,1842],{"class":610},[178,4845,4320],{"class":610},[18,4847,4849],{"id":4848},"services","Services",[164,4851,826],{"id":4852},"postgresql-16",[14,4854,4855],{},"Persistent relational database for all application data.",[145,4857,4858,4869,4877,4884],{},[148,4859,4860,562,4863,4866,4867,4575],{},[47,4861,4862],{},"Port",[53,4864,4865],{},"5432"," (bound to ",[53,4868,738],{},[148,4870,4871,562,4874],{},[47,4872,4873],{},"Default user",[53,4875,4876],{},"reqcore",[148,4878,4879,562,4882],{},[47,4880,4881],{},"Default database",[53,4883,4876],{},[148,4885,4886,562,4889,4892],{},[47,4887,4888],{},"Data volume",[53,4890,4891],{},"postgres-data"," (persists across restarts)",[164,4894,4896],{"id":4895},"minio","MinIO",[14,4898,4899],{},"S3-compatible object storage for resumes, cover letters, and file uploads.",[145,4901,4902,4913,4923,4938],{},[148,4903,4904,4907,4908,4866,4911,4575],{},[47,4905,4906],{},"S3 API",": Port ",[53,4909,4910],{},"9000",[53,4912,738],{},[148,4914,4915,4907,4918,4866,4921,4575],{},[47,4916,4917],{},"Web Console",[53,4919,4920],{},"9001",[53,4922,738],{},[148,4924,4925,4928,4929,209,4932,93,4935,4575],{},[47,4926,4927],{},"Default credentials",": Set in ",[53,4930,4931],{},".env",[53,4933,4934],{},"MINIO_ROOT_USER",[53,4936,4937],{},"MINIO_ROOT_PASSWORD",[148,4939,4940,562,4942,4892],{},[47,4941,4888],{},[53,4943,4944],{},"minio-data",[4946,4947,4948],"blockquote",{},[14,4949,4950],{},"On startup, Reqcore automatically creates the S3 bucket and enforces a private bucket policy (deletes any public access rules).",[164,4952,4954],{"id":4953},"adminer","Adminer",[14,4956,4957],{},"Lightweight database management UI for inspecting PostgreSQL data.",[145,4959,4960,4969,4975],{},[148,4961,4962,562,4964,4866,4967,4575],{},[47,4963,4862],{},[53,4965,4966],{},"8080",[53,4968,738],{},[148,4970,4971,4974],{},[47,4972,4973],{},"System",": PostgreSQL",[148,4976,4977,562,4979,4982],{},[47,4978,2066],{},[53,4980,4981],{},"postgres"," (Docker network hostname)",[18,4984,4986],{"id":4985},"security-notes","Security Notes",[145,4988,4989,4998,5001,5004],{},[148,4990,4991,4992,4994,4995,4997],{},"All Docker ports are bound to ",[53,4993,738],{}," — they are ",[47,4996,2092],{}," accessible from external networks",[148,4999,5000],{},"MinIO bucket policy is enforced as private on every application startup",[148,5002,5003],{},"Uploaded filenames are sanitized to prevent path traversal and XSS",[148,5005,5006],{},"Document access is always server-proxied — no presigned URLs are exposed to clients",[18,5008,5010],{"id":5009},"production-considerations","Production Considerations",[14,5012,5013],{},"For a production self-hosted deployment:",[712,5015,5016,5022,5030,5039,5047,5056],{},[148,5017,5018,5021],{},[47,5019,5020],{},"Put a reverse proxy in front"," (Nginx, Caddy, or Traefin) with TLS termination",[148,5023,5024,5026,5027,5029],{},[47,5025,724],{}," — the ",[53,5028,728],{}," script generates random secrets",[148,5031,5032,5035,5036,5038],{},[47,5033,5034],{},"Set up database backups"," — use ",[53,5037,748],{}," on a cron schedule",[148,5040,5041,5035,5044,5046],{},[47,5042,5043],{},"Set up S3 backup",[53,5045,752],{}," or rclone for MinIO data",[148,5048,5049,5055],{},[47,5050,5051,5052],{},"Set ",[53,5053,5054],{},"BETTER_AUTH_URL"," to your production domain",[148,5057,5058,5061],{},[47,5059,5060],{},"Consider using external PostgreSQL and S3"," for better availability",[18,5063,5065],{"id":5064},"useful-commands","Useful Commands",[169,5067,5069],{"className":1825,"code":5068,"language":1827,"meta":174,"style":174},"# View logs\ndocker compose logs -f\n\n# Restart a specific service\ndocker compose restart postgres\n\n# Reset everything (WARNING: deletes all data)\ndocker compose down -v\n\n# Run database migrations manually\nnpm run db:migrate\n\n# Open Drizzle Studio (database GUI)\nnpm run db:studio\n",[53,5070,5071,5076,5088,5092,5097,5109,5113,5118,5130,5134,5139,5147,5151,5156],{"__ignoreMap":174},[178,5072,5073],{"class":180,"line":181},[178,5074,5075],{"class":184},"# View logs\n",[178,5077,5078,5080,5082,5085],{"class":180,"line":188},[178,5079,4277],{"class":198},[178,5081,4280],{"class":610},[178,5083,5084],{"class":610}," logs",[178,5086,5087],{"class":234}," -f\n",[178,5089,5090],{"class":180,"line":225},[178,5091,404],{"emptyLinePlaceholder":403},[178,5093,5094],{"class":180,"line":259},[178,5095,5096],{"class":184},"# Restart a specific service\n",[178,5098,5099,5101,5103,5106],{"class":180,"line":273},[178,5100,4277],{"class":198},[178,5102,4280],{"class":610},[178,5104,5105],{"class":610}," restart",[178,5107,5108],{"class":610}," postgres\n",[178,5110,5111],{"class":180,"line":279},[178,5112,404],{"emptyLinePlaceholder":403},[178,5114,5115],{"class":180,"line":658},[178,5116,5117],{"class":184},"# Reset everything (WARNING: deletes all data)\n",[178,5119,5120,5122,5124,5127],{"class":180,"line":671},[178,5121,4277],{"class":198},[178,5123,4280],{"class":610},[178,5125,5126],{"class":610}," down",[178,5128,5129],{"class":234}," -v\n",[178,5131,5132],{"class":180,"line":3219},[178,5133,404],{"emptyLinePlaceholder":403},[178,5135,5136],{"class":180,"line":3225},[178,5137,5138],{"class":184},"# Run database migrations manually\n",[178,5140,5141,5143,5145],{"class":180,"line":3257},[178,5142,1839],{"class":198},[178,5144,1842],{"class":610},[178,5146,1863],{"class":610},[178,5148,5149],{"class":180,"line":3275},[178,5150,404],{"emptyLinePlaceholder":403},[178,5152,5153],{"class":180,"line":3280},[178,5154,5155],{"class":184},"# Open Drizzle Studio (database GUI)\n",[178,5157,5158,5160,5162],{"class":180,"line":3285},[178,5159,1839],{"class":198},[178,5161,1842],{"class":610},[178,5163,5164],{"class":610}," db:studio\n",[18,5166,763],{"id":762},[145,5168,5169,5176],{},[148,5170,5171,5175],{},[137,5172,5174],{"href":5173},"\u002Fdocs\u002Fdeployment\u002Frailway","Railway Deployment"," — Deploy to Railway cloud for managed hosting",[148,5177,5178,5180],{},[137,5179,785],{"href":784}," — Full reference of all configuration options",[788,5182,4715],{},{"title":174,"searchDepth":188,"depth":188,"links":5184},[5185,5186,5187,5192,5193,5194,5195],{"id":4755,"depth":188,"text":810},{"id":4764,"depth":188,"text":4765},{"id":4848,"depth":188,"text":4849,"children":5188},[5189,5190,5191],{"id":4852,"depth":225,"text":826},{"id":4895,"depth":225,"text":4896},{"id":4953,"depth":225,"text":4954},{"id":4985,"depth":188,"text":4986},{"id":5009,"depth":188,"text":5010},{"id":5064,"depth":188,"text":5065},{"id":762,"depth":188,"text":763},"Deploy Reqcore locally or on a server using Docker Compose with PostgreSQL, MinIO, and the Nuxt application.","container",{},"\u002Fdocs\u002Fdeployment\u002Fdocker-compose","Deployment",{"title":4739,"description":5196},"docs\u002F2.deployment\u002F1.docker-compose","_cJdchY6PW5BPof8AYU9bV1KZP2UseO-gPher30Yxtw",{"id":5205,"title":785,"body":5206,"description":5947,"extension":806,"icon":5948,"meta":5949,"navigation":403,"path":784,"section":5200,"seo":5950,"stem":5951,"__hash__":5952},"docs\u002Fdocs\u002F2.deployment\u002F3.environment-variables.md",{"type":7,"value":5207,"toc":5933},[5208,5210,5216,5220,5223,5352,5356,5359,5402,5406,5441,5445,5519,5523,5655,5659,5662,5710,5714,5722,5839,5843,5846,5881,5883,5911,5913,5930],[10,5209,785],{"id":4045},[14,5211,5212,5213,5215],{},"Reqcore validates all environment variables at startup using Zod schemas in ",[53,5214,2329],{},". If a required variable is missing or invalid, the application crashes immediately with a descriptive error.",[18,5217,5219],{"id":5218},"required-variables","Required Variables",[14,5221,5222],{},"These must be set for Reqcore to start:",[23,5224,5225,5236],{},[26,5226,5227],{},[29,5228,5229,5232,5234],{},[32,5230,5231],{},"Variable",[32,5233,869],{},[32,5235,872],{},[39,5237,5238,5252,5266,5279,5293,5307,5321,5335],{},[29,5239,5240,5245,5249],{},[44,5241,5242],{},[53,5243,5244],{},"DATABASE_URL",[44,5246,5247],{},[53,5248,901],{},[44,5250,5251],{},"PostgreSQL connection string",[29,5253,5254,5259,5263],{},[44,5255,5256],{},[53,5257,5258],{},"BETTER_AUTH_SECRET",[44,5260,5261],{},[53,5262,901],{},[44,5264,5265],{},"Session signing key (minimum 32 characters)",[29,5267,5268,5272,5276],{},[44,5269,5270],{},[53,5271,5054],{},[44,5273,5274],{},[53,5275,901],{},[44,5277,5278],{},"Public URL of the application",[29,5280,5281,5286,5290],{},[44,5282,5283],{},[53,5284,5285],{},"S3_ENDPOINT",[44,5287,5288],{},[53,5289,901],{},[44,5291,5292],{},"S3-compatible storage endpoint URL",[29,5294,5295,5300,5304],{},[44,5296,5297],{},[53,5298,5299],{},"S3_ACCESS_KEY",[44,5301,5302],{},[53,5303,901],{},[44,5305,5306],{},"Storage access key ID",[29,5308,5309,5314,5318],{},[44,5310,5311],{},[53,5312,5313],{},"S3_SECRET_KEY",[44,5315,5316],{},[53,5317,901],{},[44,5319,5320],{},"Storage secret access key",[29,5322,5323,5328,5332],{},[44,5324,5325],{},[53,5326,5327],{},"S3_BUCKET",[44,5329,5330],{},[53,5331,901],{},[44,5333,5334],{},"Storage bucket name",[29,5336,5337,5342,5346],{},[44,5338,5339],{},[53,5340,5341],{},"S3_REGION",[44,5343,5344],{},[53,5345,901],{},[44,5347,5348,5349,4575],{},"Storage region (e.g., ",[53,5350,5351],{},"us-east-1",[18,5353,5355],{"id":5354},"optional-variables","Optional Variables",[164,5357,582],{"id":5358},"storage",[23,5360,5361,5374],{},[26,5362,5363],{},[29,5364,5365,5367,5369,5372],{},[32,5366,5231],{},[32,5368,869],{},[32,5370,5371],{},"Default",[32,5373,872],{},[39,5375,5376],{},[29,5377,5378,5383,5387,5392],{},[44,5379,5380],{},[53,5381,5382],{},"S3_FORCE_PATH_STYLE",[44,5384,5385],{},[53,5386,1633],{},[44,5388,5389],{},[53,5390,5391],{},"true",[44,5393,5394,5395,5397,5398,5401],{},"Use path-style URLs. Set ",[53,5396,5391],{}," for MinIO, ",[53,5399,5400],{},"false"," for AWS S3 \u002F Railway Buckets",[164,5403,5405],{"id":5404},"public-site","Public Site",[23,5407,5408,5420],{},[26,5409,5410],{},[29,5411,5412,5414,5416,5418],{},[32,5413,5231],{},[32,5415,869],{},[32,5417,5371],{},[32,5419,872],{},[39,5421,5422],{},[29,5423,5424,5429,5433,5438],{},[44,5425,5426],{},[53,5427,5428],{},"NUXT_PUBLIC_SITE_URL",[44,5430,5431],{},[53,5432,901],{},[44,5434,5435],{},[53,5436,5437],{},"https:\u002F\u002Freqcore.com",[44,5439,5440],{},"Public site URL for SEO, sitemaps, and meta tags",[164,5442,5444],{"id":5443},"demo-mode","Demo Mode",[23,5446,5447,5459],{},[26,5448,5449],{},[29,5450,5451,5453,5455,5457],{},[32,5452,5231],{},[32,5454,869],{},[32,5456,5371],{},[32,5458,872],{},[39,5460,5461,5481,5500],{},[29,5462,5463,5468,5472,5478],{},[44,5464,5465],{},[53,5466,5467],{},"DEMO_ORG_SLUG",[44,5469,5470],{},[53,5471,901],{},[44,5473,5474],{},[5475,5476,5477],"em",{},"(empty)",[44,5479,5480],{},"When set, shows a read-only demo banner for this org",[29,5482,5483,5488,5492,5497],{},[44,5484,5485],{},[53,5486,5487],{},"LIVE_DEMO_EMAIL",[44,5489,5490],{},[53,5491,901],{},[44,5493,5494],{},[53,5495,5496],{},"demo@reqcore.com",[44,5498,5499],{},"Prefilled email on sign-in page (demo mode)",[29,5501,5502,5507,5511,5516],{},[44,5503,5504],{},[53,5505,5506],{},"LIVE_DEMO_SECRET",[44,5508,5509],{},[53,5510,901],{},[44,5512,5513],{},[53,5514,5515],{},"demo1234",[44,5517,5518],{},"Prefilled password on sign-in page (demo mode)",[164,5520,5522],{"id":5521},"integrations","Integrations",[23,5524,5525,5537],{},[26,5526,5527],{},[29,5528,5529,5531,5533,5535],{},[32,5530,5231],{},[32,5532,869],{},[32,5534,5371],{},[32,5536,872],{},[39,5538,5539,5562,5580,5598,5619,5637],{},[29,5540,5541,5546,5550,5554],{},[44,5542,5543],{},[53,5544,5545],{},"GOOGLE_CLIENT_ID",[44,5547,5548],{},[53,5549,901],{},[44,5551,5552],{},[5475,5553,5477],{},[44,5555,5556,5557,5561],{},"Google OAuth 2.0 Client ID for Google Calendar integration. See ",[137,5558,5560],{"href":5559},"\u002Fdocs\u002Ffeatures\u002Fgoogle-calendar","Google Calendar Integration"," for setup instructions.",[29,5563,5564,5569,5573,5577],{},[44,5565,5566],{},[53,5567,5568],{},"GOOGLE_CLIENT_SECRET",[44,5570,5571],{},[53,5572,901],{},[44,5574,5575],{},[5475,5576,5477],{},[44,5578,5579],{},"Google OAuth 2.0 Client Secret for Google Calendar integration.",[29,5581,5582,5587,5591,5595],{},[44,5583,5584],{},[53,5585,5586],{},"GITHUB_FEEDBACK_TOKEN",[44,5588,5589],{},[53,5590,901],{},[44,5592,5593],{},[5475,5594,5477],{},[44,5596,5597],{},"GitHub Personal Access Token for in-app feedback",[29,5599,5600,5605,5609,5613],{},[44,5601,5602],{},[53,5603,5604],{},"GITHUB_FEEDBACK_REPO",[44,5606,5607],{},[53,5608,901],{},[44,5610,5611],{},[5475,5612,5477],{},[44,5614,5615,5616,4575],{},"GitHub repository slug for feedback issues (e.g., ",[53,5617,5618],{},"reqcore-inc\u002Freqcore",[29,5620,5621,5626,5630,5634],{},[44,5622,5623],{},[53,5624,5625],{},"NUXT_PUBLIC_GISCUS_REPO_ID",[44,5627,5628],{},[53,5629,901],{},[44,5631,5632],{},[5475,5633,5477],{},[44,5635,5636],{},"Giscus GitHub repository node ID for comments",[29,5638,5639,5644,5648,5652],{},[44,5640,5641],{},[53,5642,5643],{},"NUXT_PUBLIC_GISCUS_CATEGORY_ID",[44,5645,5646],{},[53,5647,901],{},[44,5649,5650],{},[5475,5651,5477],{},[44,5653,5654],{},"Giscus Discussions category node ID for comments",[164,5656,5658],{"id":5657},"railway-specific","Railway-Specific",[14,5660,5661],{},"These are set automatically by Railway:",[23,5663,5664,5672],{},[26,5665,5666],{},[29,5667,5668,5670],{},[32,5669,5231],{},[32,5671,872],{},[39,5673,5674,5690,5700],{},[29,5675,5676,5681],{},[44,5677,5678],{},[53,5679,5680],{},"RAILWAY_ENVIRONMENT_NAME",[44,5682,5683,5684,238,5687,4575],{},"Name of the Railway environment (e.g., ",[53,5685,5686],{},"production",[53,5688,5689],{},"pr-42",[29,5691,5692,5697],{},[44,5693,5694],{},[53,5695,5696],{},"RAILWAY_PUBLIC_DOMAIN",[44,5698,5699],{},"Public domain assigned by Railway",[29,5701,5702,5707],{},[44,5703,5704],{},[53,5705,5706],{},"PORT",[44,5708,5709],{},"Port assigned by Railway for the service",[18,5711,5713],{"id":5712},"local-development-env","Local Development (.env)",[14,5715,1954,5716,5718,5719,5721],{},[53,5717,728],{}," script generates a ",[53,5720,4931],{}," file with sensible defaults. Example:",[169,5723,5726],{"className":5724,"code":5725,"language":2283,"meta":174,"style":174},"language-env shiki shiki-themes github-light github-dark","# Database\nDATABASE_URL=postgresql:\u002F\u002Freqcore:generated_password@localhost:5432\u002Freqcore\n\n# Authentication\nBETTER_AUTH_SECRET=generated_random_string_64_chars\nBETTER_AUTH_URL=http:\u002F\u002Flocalhost:3000\n\n# Object Storage (MinIO)\nS3_ENDPOINT=http:\u002F\u002Flocalhost:9000\nS3_ACCESS_KEY=minioadmin\nS3_SECRET_KEY=generated_password\nS3_BUCKET=reqcore\nS3_REGION=us-east-1\nS3_FORCE_PATH_STYLE=true\n\n# MinIO (Docker Compose)\nMINIO_ROOT_USER=minioadmin\nMINIO_ROOT_PASSWORD=generated_password\n\n# PostgreSQL (Docker Compose)\nPOSTGRES_USER=reqcore\nPOSTGRES_PASSWORD=generated_password\nPOSTGRES_DB=reqcore\n",[53,5727,5728,5733,5738,5742,5747,5752,5757,5761,5766,5771,5776,5781,5786,5791,5796,5800,5805,5810,5815,5819,5824,5829,5834],{"__ignoreMap":174},[178,5729,5730],{"class":180,"line":181},[178,5731,5732],{},"# Database\n",[178,5734,5735],{"class":180,"line":188},[178,5736,5737],{},"DATABASE_URL=postgresql:\u002F\u002Freqcore:generated_password@localhost:5432\u002Freqcore\n",[178,5739,5740],{"class":180,"line":225},[178,5741,404],{"emptyLinePlaceholder":403},[178,5743,5744],{"class":180,"line":259},[178,5745,5746],{},"# Authentication\n",[178,5748,5749],{"class":180,"line":273},[178,5750,5751],{},"BETTER_AUTH_SECRET=generated_random_string_64_chars\n",[178,5753,5754],{"class":180,"line":279},[178,5755,5756],{},"BETTER_AUTH_URL=http:\u002F\u002Flocalhost:3000\n",[178,5758,5759],{"class":180,"line":658},[178,5760,404],{"emptyLinePlaceholder":403},[178,5762,5763],{"class":180,"line":671},[178,5764,5765],{},"# Object Storage (MinIO)\n",[178,5767,5768],{"class":180,"line":3219},[178,5769,5770],{},"S3_ENDPOINT=http:\u002F\u002Flocalhost:9000\n",[178,5772,5773],{"class":180,"line":3225},[178,5774,5775],{},"S3_ACCESS_KEY=minioadmin\n",[178,5777,5778],{"class":180,"line":3257},[178,5779,5780],{},"S3_SECRET_KEY=generated_password\n",[178,5782,5783],{"class":180,"line":3275},[178,5784,5785],{},"S3_BUCKET=reqcore\n",[178,5787,5788],{"class":180,"line":3280},[178,5789,5790],{},"S3_REGION=us-east-1\n",[178,5792,5793],{"class":180,"line":3285},[178,5794,5795],{},"S3_FORCE_PATH_STYLE=true\n",[178,5797,5798],{"class":180,"line":3291},[178,5799,404],{"emptyLinePlaceholder":403},[178,5801,5802],{"class":180,"line":3310},[178,5803,5804],{},"# MinIO (Docker Compose)\n",[178,5806,5807],{"class":180,"line":3344},[178,5808,5809],{},"MINIO_ROOT_USER=minioadmin\n",[178,5811,5812],{"class":180,"line":3350},[178,5813,5814],{},"MINIO_ROOT_PASSWORD=generated_password\n",[178,5816,5817],{"class":180,"line":3355},[178,5818,404],{"emptyLinePlaceholder":403},[178,5820,5821],{"class":180,"line":3361},[178,5822,5823],{},"# PostgreSQL (Docker Compose)\n",[178,5825,5826],{"class":180,"line":3379},[178,5827,5828],{},"POSTGRES_USER=reqcore\n",[178,5830,5831],{"class":180,"line":3389},[178,5832,5833],{},"POSTGRES_PASSWORD=generated_password\n",[178,5835,5836],{"class":180,"line":3394},[178,5837,5838],{},"POSTGRES_DB=reqcore\n",[18,5840,5842],{"id":5841},"railway-template-variables","Railway Template Variables",[14,5844,5845],{},"When deploying on Railway, use template variables to reference other services:",[169,5847,5849],{"className":5724,"code":5848,"language":2283,"meta":174,"style":174},"DATABASE_URL=${{Postgres.DATABASE_URL}}\nS3_ENDPOINT=${{Bucket.ENDPOINT}}\nS3_ACCESS_KEY=${{Bucket.ACCESS_KEY_ID}}\nS3_SECRET_KEY=${{Bucket.SECRET_ACCESS_KEY}}\nS3_BUCKET=${{Bucket.BUCKET}}\nS3_REGION=${{Bucket.REGION}}\n",[53,5850,5851,5856,5861,5866,5871,5876],{"__ignoreMap":174},[178,5852,5853],{"class":180,"line":181},[178,5854,5855],{},"DATABASE_URL=${{Postgres.DATABASE_URL}}\n",[178,5857,5858],{"class":180,"line":188},[178,5859,5860],{},"S3_ENDPOINT=${{Bucket.ENDPOINT}}\n",[178,5862,5863],{"class":180,"line":225},[178,5864,5865],{},"S3_ACCESS_KEY=${{Bucket.ACCESS_KEY_ID}}\n",[178,5867,5868],{"class":180,"line":259},[178,5869,5870],{},"S3_SECRET_KEY=${{Bucket.SECRET_ACCESS_KEY}}\n",[178,5872,5873],{"class":180,"line":273},[178,5874,5875],{},"S3_BUCKET=${{Bucket.BUCKET}}\n",[178,5877,5878],{"class":180,"line":279},[178,5879,5880],{},"S3_REGION=${{Bucket.REGION}}\n",[18,5882,4986],{"id":4985},[145,5884,5885,5895,5900,5903],{},[148,5886,5887,5888,5890,5891,5894],{},"Never commit ",[53,5889,4931],{}," files to version control — the ",[53,5892,5893],{},".gitignore"," already excludes them",[148,5896,3099,5897,5899],{},[53,5898,728],{}," to generate cryptographically secure secrets",[148,5901,5902],{},"In production, use your platform's secret management (Railway variables, Docker secrets, etc.)",[148,5904,1954,5905,5907,5908,5910],{},[53,5906,2283],{}," utility in ",[53,5909,2329],{}," is the only sanctioned way to access environment variables in server code",[18,5912,763],{"id":762},[145,5914,5915,5920,5925],{},[148,5916,5917,5919],{},[137,5918,4745],{"href":5199}," — Self-hosted deployment",[148,5921,5922,5924],{},[137,5923,5174],{"href":5173}," — Managed cloud deployment",[148,5926,5927,5929],{},[137,5928,5],{"href":809}," — Security boundaries and best practices",[788,5931,5932],{},"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":174,"searchDepth":188,"depth":188,"links":5934},[5935,5936,5943,5944,5945,5946],{"id":5218,"depth":188,"text":5219},{"id":5354,"depth":188,"text":5355,"children":5937},[5938,5939,5940,5941,5942],{"id":5358,"depth":225,"text":582},{"id":5404,"depth":225,"text":5405},{"id":5443,"depth":225,"text":5444},{"id":5521,"depth":225,"text":5522},{"id":5657,"depth":225,"text":5658},{"id":5712,"depth":188,"text":5713},{"id":5841,"depth":188,"text":5842},{"id":4985,"depth":188,"text":4986},{"id":762,"depth":188,"text":763},"Complete reference of all environment variables used by Reqcore, including database, storage, authentication, and optional integrations.","key-round",{},{"title":785,"description":5947},"docs\u002F2.deployment\u002F3.environment-variables","gRFpAhQGvXvfMMmnsdl9NlUr-VoDzBPFGbsPetcdEDY",{"id":5954,"title":5955,"body":5956,"description":6334,"extension":806,"icon":6335,"meta":6336,"navigation":403,"path":5173,"section":5200,"seo":6337,"stem":6338,"__hash__":6339},"docs\u002Fdocs\u002F2.deployment\u002F2.railway.md","Railway",{"type":7,"value":5957,"toc":6320},[5958,5961,5968,5972,5978,5982,5986,6009,6013,6032,6036,6048,6052,6055,6174,6182,6186,6192,6210,6223,6227,6265,6269,6272,6286,6290,6304,6306,6317],[10,5959,5174],{"id":5960},"railway-deployment",[14,5962,5963,5967],{},[137,5964,5955],{"href":5965,"rel":5966},"https:\u002F\u002Frailway.app",[141]," provides managed hosting for Reqcore with automatic builds from GitHub, managed PostgreSQL, and S3-compatible storage buckets.",[18,5969,5971],{"id":5970},"architecture-on-railway","Architecture on Railway",[169,5973,5976],{"className":5974,"code":5975,"language":347},[345],"┌─────────────────────────────────────────────────────┐\n│  Cloudflare CDN                                      │\n│  • DNS (CNAME → Railway domain)                     │\n│  • DDoS protection, edge caching                    │\n│  • AI bot blocking                                  │\n└─────────────────────┬───────────────────────────────┘\n                      │ HTTPS\n┌─────────────────────┼───────────────────────────────┐\n│  Railway Project                                     │\n│  ┌──────────────────▼───────────────────────────┐   │\n│  │  Nuxt Service (auto-built from GitHub)        │   │\n│  │  Build: npm run build                         │   │\n│  │  Start: node .output\u002Fserver\u002Findex.mjs         │   │\n│  └──────────┬───────────────────┬───────────────┘   │\n│             │                   │                    │\n│  ┌──────────▼──────┐   ┌───────▼────────────────┐   │\n│  │  PostgreSQL     │   │  Storage Bucket        │   │\n│  │  (Railway DB)   │   │  (S3-compatible)       │   │\n│  └─────────────────┘   └────────────────────────┘   │\n└─────────────────────────────────────────────────────┘\n",[53,5977,5975],{"__ignoreMap":174},[18,5979,5981],{"id":5980},"setup-steps","Setup Steps",[164,5983,5985],{"id":5984},"_1-create-a-railway-project","1. Create a Railway Project",[712,5987,5988,5994,6003],{},[148,5989,5990,5991],{},"Sign in to ",[137,5992,5955],{"href":5965,"rel":5993},[141],[148,5995,5996,5997,1975,6000],{},"Click ",[47,5998,5999],{},"New Project",[47,6001,6002],{},"Deploy from GitHub repo",[148,6004,6005,6006,6008],{},"Select the ",[53,6007,5618],{}," repository (or your fork)",[164,6010,6012],{"id":6011},"_2-add-postgresql","2. Add PostgreSQL",[712,6014,6015,6027],{},[148,6016,6017,6018,1975,6021,1975,6024],{},"In your Railway project, click ",[47,6019,6020],{},"New",[47,6022,6023],{},"Database",[47,6025,6026],{},"Add PostgreSQL",[148,6028,6029,6030],{},"Railway automatically provisions the database and sets ",[53,6031,5244],{},[164,6033,6035],{"id":6034},"_3-add-storage-bucket","3. Add Storage Bucket",[712,6037,6038,6045],{},[148,6039,5996,6040,1975,6042],{},[47,6041,6020],{},[47,6043,6044],{},"Add Storage Bucket",[148,6046,6047],{},"Railway provisions S3-compatible object storage",[164,6049,6051],{"id":6050},"_4-configure-environment-variables","4. Configure Environment Variables",[14,6053,6054],{},"In the Nuxt service settings, add these variables:",[23,6056,6057,6066],{},[26,6058,6059],{},[29,6060,6061,6063],{},[32,6062,5231],{},[32,6064,6065],{},"Value",[39,6067,6068,6079,6090,6101,6112,6123,6134,6144,6153,6164],{},[29,6069,6070,6074],{},[44,6071,6072],{},[53,6073,5244],{},[44,6075,6076],{},[53,6077,6078],{},"${{Postgres.DATABASE_URL}}",[29,6080,6081,6085],{},[44,6082,6083],{},[53,6084,5285],{},[44,6086,6087],{},[53,6088,6089],{},"${{Bucket.ENDPOINT}}",[29,6091,6092,6096],{},[44,6093,6094],{},[53,6095,5299],{},[44,6097,6098],{},[53,6099,6100],{},"${{Bucket.ACCESS_KEY_ID}}",[29,6102,6103,6107],{},[44,6104,6105],{},[53,6106,5313],{},[44,6108,6109],{},[53,6110,6111],{},"${{Bucket.SECRET_ACCESS_KEY}}",[29,6113,6114,6118],{},[44,6115,6116],{},[53,6117,5327],{},[44,6119,6120],{},[53,6121,6122],{},"${{Bucket.BUCKET}}",[29,6124,6125,6129],{},[44,6126,6127],{},[53,6128,5341],{},[44,6130,6131],{},[53,6132,6133],{},"${{Bucket.REGION}}",[29,6135,6136,6140],{},[44,6137,6138],{},[53,6139,5382],{},[44,6141,6142],{},[53,6143,5400],{},[29,6145,6146,6150],{},[44,6147,6148],{},[53,6149,5258],{},[44,6151,6152],{},"Generate a 32+ char random string",[29,6154,6155,6159],{},[44,6156,6157],{},[53,6158,5054],{},[44,6160,6161],{},[53,6162,6163],{},"https:\u002F\u002Fyour-domain.com",[29,6165,6166,6170],{},[44,6167,6168],{},[53,6169,5428],{},[44,6171,6172],{},[53,6173,6163],{},[4946,6175,6176],{},[14,6177,6178,6179,6181],{},"Template variables like ",[53,6180,6078],{}," auto-resolve to the actual values from linked Railway services.",[164,6183,6185],{"id":6184},"_5-deploy","5. Deploy",[14,6187,6188,6189,6191],{},"Push to your ",[53,6190,4446],{}," branch — Railway auto-builds and deploys:",[169,6193,6195],{"className":1825,"code":6194,"language":1827,"meta":174,"style":174},"git push origin main\n",[53,6196,6197],{"__ignoreMap":174},[178,6198,6199,6201,6204,6207],{"class":180,"line":181},[178,6200,4219],{"class":198},[178,6202,6203],{"class":610}," push",[178,6205,6206],{"class":610}," origin",[178,6208,6209],{"class":610}," main\n",[14,6211,6212,6213,6216,6217,6219,6220,835],{},"Railway detects the Nuxt app via ",[53,6214,6215],{},"package.json",", runs ",[53,6218,4361],{},", and starts with ",[53,6221,6222],{},"node .output\u002Fserver\u002Findex.mjs",[18,6224,6226],{"id":6225},"custom-domain-with-cloudflare","Custom Domain with Cloudflare",[712,6228,6229,6241,6247,6250,6257],{},[148,6230,6231,6232,1975,6235,1975,6238],{},"In Railway, go to your service ",[47,6233,6234],{},"Settings",[47,6236,6237],{},"Networking",[47,6239,6240],{},"Custom Domain",[148,6242,6243,6244,4575],{},"Add your domain (e.g., ",[53,6245,6246],{},"reqcore.com",[148,6248,6249],{},"In Cloudflare, create a CNAME record pointing to the Railway-provided domain",[148,6251,6252,6253,6256],{},"Enable ",[47,6254,6255],{},"Full (strict)"," SSL mode in Cloudflare",[148,6258,6259,6260,3083,6262,6264],{},"Update ",[53,6261,5054],{},[53,6263,5428],{}," to match your domain",[18,6266,6268],{"id":6267},"preview-deployments","Preview Deployments",[14,6270,6271],{},"Railway automatically creates preview deployments for pull requests. For PR environments:",[145,6273,6274,6283],{},[148,6275,5051,6276,6278,6279,6282],{},[53,6277,5054],{}," to ",[53,6280,6281],{},"https:\u002F\u002F${{RAILWAY_PUBLIC_DOMAIN}}"," in your preview environment variables",[148,6284,6285],{},"Preview deployments get their own isolated database and storage",[18,6287,6289],{"id":6288},"monitoring","Monitoring",[145,6291,6292,6295,6301],{},[148,6293,6294],{},"Railway provides built-in logs, metrics, and deployment history",[148,6296,3099,6297,6300],{},[53,6298,6299],{},"railway logs"," CLI command for real-time log streaming",[148,6302,6303],{},"Database metrics are available in the PostgreSQL service panel",[18,6305,763],{"id":762},[145,6307,6308,6312],{},[148,6309,6310,5180],{},[137,6311,785],{"href":784},[148,6313,6314,6316],{},[137,6315,771],{"href":770}," — Understand the system design",[788,6318,6319],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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":174,"searchDepth":188,"depth":188,"links":6321},[6322,6323,6330,6331,6332,6333],{"id":5970,"depth":188,"text":5971},{"id":5980,"depth":188,"text":5981,"children":6324},[6325,6326,6327,6328,6329],{"id":5984,"depth":225,"text":5985},{"id":6011,"depth":225,"text":6012},{"id":6034,"depth":225,"text":6035},{"id":6050,"depth":225,"text":6051},{"id":6184,"depth":225,"text":6185},{"id":6225,"depth":188,"text":6226},{"id":6267,"depth":188,"text":6268},{"id":6288,"depth":188,"text":6289},{"id":762,"depth":188,"text":763},"Deploy Reqcore to Railway with managed PostgreSQL, S3-compatible storage, and automatic builds from GitHub.","train",{},{"title":5955,"description":6334},"docs\u002F2.deployment\u002F2.railway","7qJtB3vk8QwcsWiNPe1aVTdb4dZdT385iojuv6tzpos",{"id":6341,"title":6342,"body":6343,"description":6764,"extension":806,"icon":6765,"meta":6766,"navigation":403,"path":6767,"section":6768,"seo":6769,"stem":6770,"__hash__":6771},"docs\u002Fdocs\u002F3.features\u002F5.application-forms.md","Application Forms",{"type":7,"value":6344,"toc":6750},[6345,6348,6351,6355,6358,6481,6485,6489,6534,6538,6541,6545,6548,6552,6559,6565,6568,6591,6595,6615,6619,6632,6636,6725,6727],[10,6346,6342],{"id":6347},"application-forms",[14,6349,6350],{},"Recruiters can configure custom questions for each job. Applicants see these questions on the public application form along with standard fields (name, email, resume).",[18,6352,6354],{"id":6353},"question-types","Question Types",[14,6356,6357],{},"Reqcore supports nine question types:",[23,6359,6360,6371],{},[26,6361,6362],{},[29,6363,6364,6366,6368],{},[32,6365,869],{},[32,6367,872],{},[32,6369,6370],{},"Input",[39,6372,6373,6385,6397,6409,6421,6433,6445,6457,6469],{},[29,6374,6375,6379,6382],{},[44,6376,6377],{},[53,6378,1598],{},[44,6380,6381],{},"Single-line text answer",[44,6383,6384],{},"Text input",[29,6386,6387,6391,6394],{},[44,6388,6389],{},[53,6390,1601],{},[44,6392,6393],{},"Multi-line text answer",[44,6395,6396],{},"Textarea",[29,6398,6399,6403,6406],{},[44,6400,6401],{},[53,6402,1604],{},[44,6404,6405],{},"Choose one option from a list",[44,6407,6408],{},"Dropdown",[29,6410,6411,6415,6418],{},[44,6412,6413],{},[53,6414,1607],{},[44,6416,6417],{},"Choose multiple options from a list",[44,6419,6420],{},"Checkboxes",[29,6422,6423,6427,6430],{},[44,6424,6425],{},[53,6426,1610],{},[44,6428,6429],{},"Numeric value",[44,6431,6432],{},"Number input",[29,6434,6435,6439,6442],{},[44,6436,6437],{},[53,6438,1613],{},[44,6440,6441],{},"Calendar date",[44,6443,6444],{},"Date picker",[29,6446,6447,6451,6454],{},[44,6448,6449],{},[53,6450,1616],{},[44,6452,6453],{},"Web link",[44,6455,6456],{},"URL input",[29,6458,6459,6463,6466],{},[44,6460,6461],{},[53,6462,1162],{},[44,6464,6465],{},"Email address",[44,6467,6468],{},"Email input",[29,6470,6471,6475,6478],{},[44,6472,6473],{},[53,6474,1621],{},[44,6476,6477],{},"Upload a file (portfolio, cover letter, etc.)",[44,6479,6480],{},"File input",[18,6482,6484],{"id":6483},"managing-questions","Managing Questions",[164,6486,6488],{"id":6487},"from-the-dashboard","From the Dashboard",[712,6490,6491,6498,6504,6531],{},[148,6492,6493,6494,6497],{},"Navigate to the job's ",[47,6495,6496],{},"Application Form"," tab",[148,6499,5996,6500,6503],{},[47,6501,6502],{},"Add Question"," to create a new question",[148,6505,6506,6507],{},"Configure:\n",[145,6508,6509,6514,6519,6525],{},[148,6510,6511,6513],{},[47,6512,1582],{}," — What you're asking",[148,6515,6516,6518],{},[47,6517,869],{}," — The input type",[148,6520,6521,6524],{},[47,6522,6523],{},"Required"," — Whether the applicant must answer",[148,6526,6527,6530],{},[47,6528,6529],{},"Options"," — For select question types, define the choices",[148,6532,6533],{},"Drag questions to reorder them",[164,6535,6537],{"id":6536},"reordering","Reordering",[14,6539,6540],{},"Questions can be reordered via drag-and-drop. The order is saved to the database using a bulk reorder endpoint and reflected on the public application form.",[164,6542,6544],{"id":6543},"editing-and-deleting","Editing and Deleting",[14,6546,6547],{},"Each question can be edited or deleted from the Application Form tab. Deleting a question does not delete existing responses from past applications.",[18,6549,6551],{"id":6550},"public-application-form","Public Application Form",[14,6553,6554,6555,6558],{},"When a job is ",[47,6556,6557],{},"Open",", a shareable application link is generated. The URL format is:",[169,6560,6563],{"className":6561,"code":6562,"language":347},[345],"\u002Fjobs\u002F{slug}\u002Fapply\n",[53,6564,6562],{"__ignoreMap":174},[14,6566,6567],{},"The form consists of:",[712,6569,6570,6576,6582],{},[148,6571,6572,6575],{},[47,6573,6574],{},"Standard fields",": Name, email (always present)",[148,6577,6578,6581],{},[47,6579,6580],{},"Custom questions",": Rendered dynamically based on the job's configuration",[148,6583,6584,6587,6588,6590],{},[47,6585,6586],{},"File uploads",": Resume upload (standard) + any ",[53,6589,1621],{}," questions",[164,6592,6594],{"id":6593},"submission-flow","Submission Flow",[712,6596,6597,6600,6603,6606,6609,6612],{},[148,6598,6599],{},"Applicant fills in all fields",[148,6601,6602],{},"Files are validated (MIME type via magic bytes) and uploaded to S3",[148,6604,6605],{},"A candidate record is created (or the existing one is reused if the email matches)",[148,6607,6608],{},"An application record links the candidate to the job",[148,6610,6611],{},"Question responses are stored per application",[148,6613,6614],{},"Applicant sees a confirmation page",[164,6616,6618],{"id":6617},"anti-spam","Anti-Spam",[145,6620,6621,6627],{},[148,6622,6623,6626],{},[47,6624,6625],{},"Honeypot field"," — Hidden field that bots fill in; submissions with this field are silently rejected",[148,6628,6629,6631],{},[47,6630,103],{}," — IP-based sliding window limits on the submission endpoint",[18,6633,6635],{"id":6634},"api-endpoints","API Endpoints",[23,6637,6638,6650],{},[26,6639,6640],{},[29,6641,6642,6645,6648],{},[32,6643,6644],{},"Method",[32,6646,6647],{},"Path",[32,6649,872],{},[39,6651,6652,6667,6681,6696,6710],{},[29,6653,6654,6659,6664],{},[44,6655,6656],{},[53,6657,6658],{},"GET",[44,6660,6661],{},[53,6662,6663],{},"\u002Fapi\u002Fjobs\u002F:id\u002Fquestions",[44,6665,6666],{},"List custom questions (org-scoped)",[29,6668,6669,6674,6678],{},[44,6670,6671],{},[53,6672,6673],{},"POST",[44,6675,6676],{},[53,6677,6663],{},[44,6679,6680],{},"Add a question",[29,6682,6683,6688,6693],{},[44,6684,6685],{},[53,6686,6687],{},"PATCH",[44,6689,6690],{},[53,6691,6692],{},"\u002Fapi\u002Fjobs\u002F:id\u002Fquestions\u002F:qid",[44,6694,6695],{},"Update a question",[29,6697,6698,6703,6707],{},[44,6699,6700],{},[53,6701,6702],{},"DELETE",[44,6704,6705],{},[53,6706,6692],{},[44,6708,6709],{},"Delete a question",[29,6711,6712,6717,6722],{},[44,6713,6714],{},[53,6715,6716],{},"PUT",[44,6718,6719],{},[53,6720,6721],{},"\u002Fapi\u002Fjobs\u002F:id\u002Fquestions\u002Freorder",[44,6723,6724],{},"Reorder questions",[18,6726,763],{"id":762},[145,6728,6729,6736,6743],{},[148,6730,6731,6735],{},[137,6732,6734],{"href":6733},"\u002Fdocs\u002Ffeatures\u002Fpublic-job-board","Public Job Board"," — How applicants discover your jobs",[148,6737,6738,6742],{},[137,6739,6741],{"href":6740},"\u002Fdocs\u002Ffeatures\u002Fcandidate-pipeline","Candidate Pipeline"," — Track applicants through stages",[148,6744,6745,6749],{},[137,6746,6748],{"href":6747},"\u002Fdocs\u002Ffeatures\u002Fdocument-storage","Document Storage"," — How uploaded files are stored securely",{"title":174,"searchDepth":188,"depth":188,"links":6751},[6752,6753,6758,6762,6763],{"id":6353,"depth":188,"text":6354},{"id":6483,"depth":188,"text":6484,"children":6754},[6755,6756,6757],{"id":6487,"depth":225,"text":6488},{"id":6536,"depth":225,"text":6537},{"id":6543,"depth":225,"text":6544},{"id":6550,"depth":188,"text":6551,"children":6759},[6760,6761],{"id":6593,"depth":225,"text":6594},{"id":6617,"depth":225,"text":6618},{"id":6634,"depth":188,"text":6635},{"id":762,"depth":188,"text":763},"Create custom application questions per job including text fields, selects, file uploads, and more. Applicants answer them on the public form.","clipboard-list",{},"\u002Fdocs\u002Ffeatures\u002Fapplication-forms","Features",{"title":6342,"description":6764},"docs\u002F3.features\u002F5.application-forms","-2WjlTxUz4iZNWlkXEuyXbBgj2fn8zZClTUGy83oZiE",{"id":6773,"title":6741,"body":6774,"description":7125,"extension":806,"icon":7126,"meta":7127,"navigation":403,"path":6740,"section":6768,"seo":7128,"stem":7129,"__hash__":7130},"docs\u002Fdocs\u002F3.features\u002F2.candidate-pipeline.md",{"type":7,"value":6775,"toc":7109},[6776,6779,6782,6786,6789,6795,6867,6871,6878,6892,6896,6899,6931,6935,6939,6942,6960,6964,6967,6971,6976,6987,6991,6995,6998,7009,7013,7018,7020,7088,7090],[10,6777,6741],{"id":6778},"candidate-pipeline",[14,6780,6781],{},"Reqcore's pipeline gives recruiters a visual Kanban board to track candidates through hiring stages. Each job has its own pipeline view.",[18,6783,6785],{"id":6784},"pipeline-stages","Pipeline Stages",[14,6787,6788],{},"Applications move through these stages:",[169,6790,6793],{"className":6791,"code":6792,"language":347},[345],"New → Screening → Interview → Offer → Hired\n                                     → Rejected (from any stage)\n",[53,6794,6792],{"__ignoreMap":174},[23,6796,6797,6806],{},[26,6798,6799],{},[29,6800,6801,6804],{},[32,6802,6803],{},"Stage",[32,6805,872],{},[39,6807,6808,6817,6827,6837,6847,6857],{},[29,6809,6810,6814],{},[44,6811,6812],{},[47,6813,6020],{},[44,6815,6816],{},"Application just received, not yet reviewed",[29,6818,6819,6824],{},[44,6820,6821],{},[47,6822,6823],{},"Screening",[44,6825,6826],{},"Recruiter is reviewing the application",[29,6828,6829,6834],{},[44,6830,6831],{},[47,6832,6833],{},"Interview",[44,6835,6836],{},"Candidate is being interviewed",[29,6838,6839,6844],{},[44,6840,6841],{},[47,6842,6843],{},"Offer",[44,6845,6846],{},"Offer has been extended",[29,6848,6849,6854],{},[44,6850,6851],{},[47,6852,6853],{},"Hired",[44,6855,6856],{},"Candidate accepted and hired",[29,6858,6859,6864],{},[44,6860,6861],{},[47,6862,6863],{},"Rejected",[44,6865,6866],{},"Candidate was not selected (can happen at any stage)",[18,6868,6870],{"id":6869},"kanban-board","Kanban Board",[14,6872,6873,6874,6877],{},"Access the pipeline from any job's ",[47,6875,6876],{},"Pipeline"," tab. The board shows:",[145,6879,6880,6883,6886,6889],{},[148,6881,6882],{},"Color-coded columns for each stage",[148,6884,6885],{},"Candidate cards with name, email, and current status",[148,6887,6888],{},"Drag-and-drop between columns to update status",[148,6890,6891],{},"Click any card to open the candidate detail sidebar",[18,6893,6895],{"id":6894},"candidate-detail-sidebar","Candidate Detail Sidebar",[14,6897,6898],{},"Clicking a candidate card opens a slide-over panel with:",[145,6900,6901,6907,6913,6919,6925],{},[148,6902,6903,6906],{},[47,6904,6905],{},"Status transitions"," — Move the candidate to the next stage",[148,6908,6909,6912],{},[47,6910,6911],{},"Notes"," — Add recruiter\u002Fhiring manager notes",[148,6914,6915,6918],{},[47,6916,6917],{},"Question responses"," — View answers to custom application questions",[148,6920,6921,6924],{},[47,6922,6923],{},"Documents"," — View uploaded resumes and files",[148,6926,6927,6930],{},[47,6928,6929],{},"PDF preview"," — Inline preview of PDF documents",[18,6932,6934],{"id":6933},"managing-candidates","Managing Candidates",[164,6936,6938],{"id":6937},"adding-candidates","Adding Candidates",[14,6940,6941],{},"Candidates can be added in two ways:",[712,6943,6944,6950],{},[148,6945,6946,6949],{},[47,6947,6948],{},"Public application"," — When someone applies through the public job page, a candidate record is automatically created (deduplicated by email within the organization)",[148,6951,6952,6955,6956,6959],{},[47,6953,6954],{},"Manual creation"," — Recruiters can add candidates directly from the ",[47,6957,6958],{},"Candidates"," section in the sidebar",[164,6961,6963],{"id":6962},"candidate-deduplication","Candidate Deduplication",[14,6965,6966],{},"Candidates are deduplicated by email within each organization. If a candidate applies to multiple jobs, a single candidate record is maintained with multiple application records.",[164,6968,6970],{"id":6969},"per-job-candidates-table","Per-Job Candidates Table",[14,6972,1954,6973,6975],{},[47,6974,6958],{}," tab on each job shows a data table view (similar to Supabase's table UI) with:",[145,6977,6978,6981,6984],{},[148,6979,6980],{},"Sortable columns",[148,6982,6983],{},"Click-to-open detail sidebar",[148,6985,6986],{},"Bulk status overview",[18,6988,6990],{"id":6989},"applications","Applications",[164,6992,6994],{"id":6993},"creating-applications","Creating Applications",[14,6996,6997],{},"Applications link a candidate to a job. They can be created:",[145,6999,7000,7003,7006],{},[148,7001,7002],{},"Through the public application form",[148,7004,7005],{},"Via the \"Apply candidate to job\" modal from the job detail page",[148,7007,7008],{},"Via the \"Apply to job\" modal from the candidate detail page",[164,7010,7012],{"id":7011},"unique-constraint","Unique Constraint",[14,7014,7015,7016,835],{},"Each candidate can only have one application per job within an organization. This is enforced at the database level with a unique constraint on ",[53,7017,1372],{},[18,7019,6635],{"id":6634},[23,7021,7022,7032],{},[26,7023,7024],{},[29,7025,7026,7028,7030],{},[32,7027,6644],{},[32,7029,6647],{},[32,7031,872],{},[39,7033,7034,7048,7061,7075],{},[29,7035,7036,7040,7045],{},[44,7037,7038],{},[53,7039,6658],{},[44,7041,7042],{},[53,7043,7044],{},"\u002Fapi\u002Fapplications",[44,7046,7047],{},"List applications (filterable by job, status, candidate)",[29,7049,7050,7054,7058],{},[44,7051,7052],{},[53,7053,6673],{},[44,7055,7056],{},[53,7057,7044],{},[44,7059,7060],{},"Create an application",[29,7062,7063,7067,7072],{},[44,7064,7065],{},[53,7066,6658],{},[44,7068,7069],{},[53,7070,7071],{},"\u002Fapi\u002Fapplications\u002F:id",[44,7073,7074],{},"Application detail with candidate, job, and responses",[29,7076,7077,7081,7085],{},[44,7078,7079],{},[53,7080,6687],{},[44,7082,7083],{},[53,7084,7071],{},[44,7086,7087],{},"Update status, notes, or score",[18,7089,763],{"id":762},[145,7091,7092,7097,7102],{},[148,7093,7094,7096],{},[137,7095,6342],{"href":6767}," — Configure custom questions per job",[148,7098,7099,7101],{},[137,7100,6748],{"href":6747}," — Manage resumes and files",[148,7103,7104,7108],{},[137,7105,7107],{"href":7106},"\u002Fdocs\u002Ffeatures\u002Fdashboard","Dashboard"," — At-a-glance hiring overview",{"title":174,"searchDepth":188,"depth":188,"links":7110},[7111,7112,7113,7114,7119,7123,7124],{"id":6784,"depth":188,"text":6785},{"id":6869,"depth":188,"text":6870},{"id":6894,"depth":188,"text":6895},{"id":6933,"depth":188,"text":6934,"children":7115},[7116,7117,7118],{"id":6937,"depth":225,"text":6938},{"id":6962,"depth":225,"text":6963},{"id":6969,"depth":225,"text":6970},{"id":6989,"depth":188,"text":6990,"children":7120},[7121,7122],{"id":6993,"depth":225,"text":6994},{"id":7011,"depth":225,"text":7012},{"id":6634,"depth":188,"text":6635},{"id":762,"depth":188,"text":763},"Track candidates through hiring stages with a drag-and-drop Kanban board. Covers status transitions, scoring, and notes.","users",{},{"title":6741,"description":7125},"docs\u002F3.features\u002F2.candidate-pipeline","0Y7uzYVrsomFTwOTxl1osYqBqy2qaOF5Z48hIN7s-Ho",{"id":7132,"title":7107,"body":7133,"description":7389,"extension":806,"icon":7390,"meta":7391,"navigation":403,"path":7106,"section":6768,"seo":7392,"stem":7393,"__hash__":7394},"docs\u002Fdocs\u002F3.features\u002F6.dashboard.md",{"type":7,"value":7134,"toc":7376},[7135,7138,7141,7145,7149,7152,7224,7228,7231,7242,7246,7249,7257,7261,7264,7277,7281,7284,7295,7299,7302,7319,7323,7326,7338,7342,7345,7351,7354,7356],[10,7136,7107],{"id":7137},"dashboard",[14,7139,7140],{},"The recruiter dashboard provides a high-level overview of your organization's hiring activity. It's the first screen you see after signing in.",[18,7142,7144],{"id":7143},"dashboard-widgets","Dashboard Widgets",[164,7146,7148],{"id":7147},"stat-cards","Stat Cards",[14,7150,7151],{},"Four key metrics displayed as clickable cards:",[23,7153,7154,7166],{},[26,7155,7156],{},[29,7157,7158,7161,7163],{},[32,7159,7160],{},"Metric",[32,7162,872],{},[32,7164,7165],{},"Click Action",[39,7167,7168,7184,7196,7208],{},[29,7169,7170,7175,7181],{},[44,7171,7172],{},[47,7173,7174],{},"Open Jobs",[44,7176,7177,7178,7180],{},"Number of jobs in ",[53,7179,952],{}," status",[44,7182,7183],{},"Navigate to filtered jobs list",[29,7185,7186,7190,7193],{},[44,7187,7188],{},[47,7189,6958],{},[44,7191,7192],{},"Total candidates in the organization",[44,7194,7195],{},"Navigate to candidates list",[29,7197,7198,7202,7205],{},[44,7199,7200],{},[47,7201,6990],{},[44,7203,7204],{},"Total applications received",[44,7206,7207],{},"Navigate to applications list",[29,7209,7210,7215,7221],{},[44,7211,7212],{},[47,7213,7214],{},"Unreviewed",[44,7216,7217,7218,7220],{},"Applications in ",[53,7219,1309],{}," status, not yet screened",[44,7222,7223],{},"Navigate to filtered applications",[164,7225,7227],{"id":7226},"pipeline-overview","Pipeline Overview",[14,7229,7230],{},"A stacked bar chart showing the distribution of applications across pipeline stages:",[145,7232,7233,7236,7239],{},[148,7234,7235],{},"Color-coded segments for each status (New, Screening, Interview, Offer, Hired, Rejected)",[148,7237,7238],{},"Legend with counts per status",[148,7240,7241],{},"Visual representation of your pipeline health",[164,7243,7245],{"id":7244},"jobs-by-status","Jobs by Status",[14,7247,7248],{},"Counts of jobs in each lifecycle stage:",[145,7250,7251,7254],{},[148,7252,7253],{},"Draft, Open, Closed, Archived",[148,7255,7256],{},"Quick way to see how many requisitions are active",[164,7258,7260],{"id":7259},"recent-applications","Recent Applications",[14,7262,7263],{},"The last 10 applications received, showing:",[145,7265,7266,7269,7271,7274],{},[148,7267,7268],{},"Candidate name and email",[148,7270,918],{},[148,7272,7273],{},"Application status badge",[148,7275,7276],{},"Relative timestamp (\"2 hours ago\", \"3 days ago\")",[164,7278,7280],{"id":7279},"top-active-jobs","Top Active Jobs",[14,7282,7283],{},"The top 5 open jobs ranked by application count:",[145,7285,7286,7289,7292],{},[148,7287,7288],{},"Job title and total applications",[148,7290,7291],{},"New\u002Funreviewed badges for jobs needing attention",[148,7293,7294],{},"Quick link to each job's pipeline",[18,7296,7298],{"id":7297},"empty-state","Empty State",[14,7300,7301],{},"New organizations with no data see a welcome screen with:",[145,7303,7304,7307,7313],{},[148,7305,7306],{},"A brief explanation of what Reqcore provides",[148,7308,7309,7312],{},[47,7310,7311],{},"Create Job"," call-to-action to get started",[148,7314,7315,7318],{},[47,7316,7317],{},"Add Candidate"," secondary action",[18,7320,7322],{"id":7321},"navigation","Navigation",[14,7324,7325],{},"The dashboard is accessible from:",[145,7327,7328,7333],{},[148,7329,1954,7330,7332],{},[47,7331,7107],{}," link in the sidebar (always visible)",[148,7334,1954,7335,7337],{},[47,7336,7107],{}," button in the top-right of public pages (when signed in)",[18,7339,7341],{"id":7340},"api","API",[14,7343,7344],{},"The dashboard aggregates data from a single endpoint:",[169,7346,7349],{"className":7347,"code":7348,"language":347},[345],"GET \u002Fapi\u002Fdashboard\u002Fstats\n",[53,7350,7348],{"__ignoreMap":174},[14,7352,7353],{},"This returns all counts, pipeline breakdown, jobs by status, recent applications, and top active jobs — all scoped to the user's active organization.",[18,7355,763],{"id":762},[145,7357,7358,7365,7370],{},[148,7359,7360,7364],{},[137,7361,7363],{"href":7362},"\u002Fdocs\u002Ffeatures\u002Fjob-management","Job Management"," — Create and manage jobs",[148,7366,7367,7369],{},[137,7368,6741],{"href":6740}," — Track candidates through stages",[148,7371,7372,7375],{},[137,7373,4765],{"href":7374},"\u002Fdocs\u002Fgetting-started\u002Fquick-start"," — Set up your first job",{"title":174,"searchDepth":188,"depth":188,"links":7377},[7378,7385,7386,7387,7388],{"id":7143,"depth":188,"text":7144,"children":7379},[7380,7381,7382,7383,7384],{"id":7147,"depth":225,"text":7148},{"id":7226,"depth":225,"text":7227},{"id":7244,"depth":225,"text":7245},{"id":7259,"depth":225,"text":7260},{"id":7279,"depth":225,"text":7280},{"id":7297,"depth":188,"text":7298},{"id":7321,"depth":188,"text":7322},{"id":7340,"depth":188,"text":7341},{"id":762,"depth":188,"text":763},"At-a-glance recruiter dashboard with stat cards, pipeline breakdown, recent applications, and top active jobs.","layout-dashboard",{},{"title":7107,"description":7389},"docs\u002F3.features\u002F6.dashboard","-M2fkWTAwKJfwOHVyVU6fiGRHdaX0rA8X6xRaewImvA",{"id":7396,"title":6748,"body":7397,"description":7788,"extension":806,"icon":7789,"meta":7790,"navigation":403,"path":6747,"section":6768,"seo":7791,"stem":7792,"__hash__":7793},"docs\u002Fdocs\u002F3.features\u002F3.document-storage.md",{"type":7,"value":7398,"toc":7774},[7399,7402,7405,7409,7442,7446,7449,7466,7470,7481,7485,7489,7492,7505,7509,7512,7535,7539,7616,7618,7688,7692,7752,7755,7757],[10,7400,6748],{"id":7401},"document-storage",[14,7403,7404],{},"Reqcore stores candidate documents (resumes, cover letters, work samples) in an S3-compatible object store. Documents are always accessed through authenticated server-proxied endpoints — never via direct S3 URLs.",[18,7406,7408],{"id":7407},"how-it-works","How It Works",[712,7410,7411,7417,7425,7431,7436],{},[148,7412,7413,7416],{},[47,7414,7415],{},"Upload"," — Files are sent as multipart\u002Fform-data to the server",[148,7418,7419,7422,7423,485],{},[47,7420,7421],{},"Validation"," — MIME type is verified using magic bytes (not just the ",[53,7424,484],{},[148,7426,7427,7430],{},[47,7428,7429],{},"Sanitization"," — Filenames are sanitized to prevent path traversal and XSS",[148,7432,7433,7435],{},[47,7434,582],{}," — Files are stored in the S3 bucket with a unique storage key",[148,7437,7438,7441],{},[47,7439,7440],{},"Access"," — Download and preview are always server-proxied through authenticated endpoints",[18,7443,7445],{"id":7444},"uploading-documents","Uploading Documents",[14,7447,7448],{},"Documents can be uploaded from:",[145,7450,7451,7457],{},[148,7452,7453,7456],{},[47,7454,7455],{},"Candidate detail page"," — Upload resumes and cover letters for any candidate",[148,7458,7459,7462,7463,7465],{},[47,7460,7461],{},"Public application form"," — Applicants can upload files when ",[53,7464,1621],{}," question type is configured",[164,7467,7469],{"id":7468},"limits","Limits",[145,7471,7472,7475,7478],{},[148,7473,7474],{},"Maximum 20 documents per candidate (enforced on public application endpoint)",[148,7476,7477],{},"File type validation via magic bytes (prevents uploading disguised executables)",[148,7479,7480],{},"Filename sanitization strips dangerous characters and path segments",[18,7482,7484],{"id":7483},"viewing-documents","Viewing Documents",[164,7486,7488],{"id":7487},"download","Download",[14,7490,7491],{},"Documents can be downloaded via the candidate detail page. The server streams the file from S3 with appropriate headers:",[145,7493,7494,7500],{},[148,7495,7496,7499],{},[53,7497,7498],{},"Content-Disposition: attachment"," for downloads",[148,7501,7502,7504],{},[53,7503,547],{}," to prevent caching of sensitive documents",[164,7506,7508],{"id":7507},"pdf-preview","PDF Preview",[14,7510,7511],{},"PDF files can be previewed inline in the browser:",[145,7513,7514,7517,7522,7525],{},[148,7515,7516],{},"Preview renders in a same-origin iframe",[148,7518,531,7519,7521],{},[53,7520,534],{}," files support inline preview",[148,7523,7524],{},"DOC\u002FDOCX files (which may contain macros) must be downloaded, not previewed",[148,7526,7527,7528,7530,7531,7534],{},"The preview endpoint uses ",[53,7529,680],{}," (overriding the global ",[53,7532,7533],{},"DENY"," policy)",[18,7536,7538],{"id":7537},"security-model","Security Model",[23,7540,7541,7549],{},[26,7542,7543],{},[29,7544,7545,7547],{},[32,7546,446],{},[32,7548,872],{},[39,7550,7551,7561,7570,7579,7588,7597,7606],{},[29,7552,7553,7558],{},[44,7554,7555],{},[47,7556,7557],{},"Private bucket",[44,7559,7560],{},"S3 bucket policy enforced as private on every startup",[29,7562,7563,7567],{},[44,7564,7565],{},[47,7566,468],{},[44,7568,7569],{},"No presigned URLs are ever exposed to clients",[29,7571,7572,7576],{},[44,7573,7574],{},[47,7575,478],{},[44,7577,7578],{},"Magic byte inspection prevents type spoofing",[29,7580,7581,7585],{},[44,7582,7583],{},[47,7584,492],{},[44,7586,7587],{},"Strips path traversal sequences, XSS payloads, and filesystem-unsafe characters",[29,7589,7590,7594],{},[44,7591,7592],{},[47,7593,505],{},[44,7595,7596],{},"The internal S3 object key is never included in API responses",[29,7598,7599,7604],{},[44,7600,7601],{},[47,7602,7603],{},"Per-candidate limits",[44,7605,521],{},[29,7607,7608,7612],{},[44,7609,7610],{},[47,7611,126],{},[44,7613,7614,548],{},[53,7615,547],{},[18,7617,6635],{"id":6634},[23,7619,7620,7630],{},[26,7621,7622],{},[29,7623,7624,7626,7628],{},[32,7625,6644],{},[32,7627,6647],{},[32,7629,872],{},[39,7631,7632,7646,7660,7674],{},[29,7633,7634,7638,7643],{},[44,7635,7636],{},[53,7637,6673],{},[44,7639,7640],{},[53,7641,7642],{},"\u002Fapi\u002Fcandidates\u002F:id\u002Fdocuments",[44,7644,7645],{},"Upload a document",[29,7647,7648,7652,7657],{},[44,7649,7650],{},[53,7651,6658],{},[44,7653,7654],{},[53,7655,7656],{},"\u002Fapi\u002Fdocuments\u002F:id\u002Fdownload",[44,7658,7659],{},"Download a document (streamed)",[29,7661,7662,7666,7671],{},[44,7663,7664],{},[53,7665,6658],{},[44,7667,7668],{},[53,7669,7670],{},"\u002Fapi\u002Fdocuments\u002F:id\u002Fpreview",[44,7672,7673],{},"Inline PDF preview (streamed)",[29,7675,7676,7680,7685],{},[44,7677,7678],{},[53,7679,6702],{},[44,7681,7682],{},[53,7683,7684],{},"\u002Fapi\u002Fdocuments\u002F:id",[44,7686,7687],{},"Delete a document (S3 + database)",[18,7689,7691],{"id":7690},"storage-backends","Storage Backends",[23,7693,7694,7707],{},[26,7695,7696],{},[29,7697,7698,7701,7704],{},[32,7699,7700],{},"Backend",[32,7702,7703],{},"Use Case",[32,7705,7706],{},"Config",[39,7708,7709,7723,7738],{},[29,7710,7711,7715,7718],{},[44,7712,7713],{},[47,7714,4896],{},[44,7716,7717],{},"Local development",[44,7719,7720],{},[53,7721,7722],{},"S3_FORCE_PATH_STYLE=true",[29,7724,7725,7730,7733],{},[44,7726,7727],{},[47,7728,7729],{},"Railway Buckets",[44,7731,7732],{},"Railway deployments",[44,7734,7735],{},[53,7736,7737],{},"S3_FORCE_PATH_STYLE=false",[29,7739,7740,7745,7748],{},[44,7741,7742],{},[47,7743,7744],{},"AWS S3",[44,7746,7747],{},"Self-hosted production",[44,7749,7750],{},[53,7751,7737],{},[14,7753,7754],{},"Any S3-compatible storage service works — configure the endpoint, credentials, and bucket name in your environment variables.",[18,7756,763],{"id":762},[145,7758,7759,7764,7769],{},[148,7760,7761,7763],{},[137,7762,6342],{"href":6767}," — Add file upload questions to your forms",[148,7765,7766,7768],{},[137,7767,5],{"href":809}," — Full security model overview",[148,7770,7771,7773],{},[137,7772,785],{"href":784}," — Storage configuration reference",{"title":174,"searchDepth":188,"depth":188,"links":7775},[7776,7777,7780,7784,7785,7786,7787],{"id":7407,"depth":188,"text":7408},{"id":7444,"depth":188,"text":7445,"children":7778},[7779],{"id":7468,"depth":225,"text":7469},{"id":7483,"depth":188,"text":7484,"children":7781},[7782,7783],{"id":7487,"depth":225,"text":7488},{"id":7507,"depth":225,"text":7508},{"id":7537,"depth":188,"text":7538},{"id":6634,"depth":188,"text":6635},{"id":7690,"depth":188,"text":7691},{"id":762,"depth":188,"text":763},"Upload, preview, and manage candidate resumes and cover letters via S3-compatible storage in Reqcore.","file-text",{},{"title":6748,"description":7788},"docs\u002F3.features\u002F3.document-storage","k6LhcCyZxbOI0QxA9T_tQiHjwTXqoyqw30J3l8lsyK0",{"id":7795,"title":5560,"body":7796,"description":8481,"extension":806,"icon":8482,"meta":8483,"navigation":403,"path":5559,"section":6768,"seo":8484,"stem":8485,"__hash__":8486},"docs\u002Fdocs\u002F3.features\u002F7.google-calendar.md",{"type":7,"value":7797,"toc":8460},[7798,7801,7804,7807,7824,7828,7888,7899,7902,7906,7913,7916,7920,7948,7952,7972,7976,8033,8038,8045,8077,8085,8089,8095,8103,8110,8114,8199,8203,8211,8226,8241,8275,8284,8288,8291,8308,8310,8313,8316,8335,8338,8352,8357,8359,8363,8367,8374,8378,8383,8389,8399,8403,8409,8413,8420,8424,8430,8433,8435,8439,8457],[10,7799,5560],{"id":7800},"google-calendar-integration",[14,7802,7803],{},"Reqcore integrates with Google Calendar to keep interview schedules in sync automatically. When an interview is scheduled, a calendar event is created and both the interviewer and candidate are added as attendees. RSVP responses from Google Calendar sync back to Reqcore in real-time via webhooks.",[18,7805,7806],{"id":7407},"How it works",[712,7808,7809,7812,7818,7821],{},[148,7810,7811],{},"An admin configures a Google OAuth app once at the server level (self-hosted only — reqcore.com handles this for cloud users)",[148,7813,7814,7815],{},"Each user connects their own Google account from ",[47,7816,7817],{},"Settings → Integrations",[148,7819,7820],{},"Reqcore creates calendar events on that user's calendar when interviews are scheduled",[148,7822,7823],{},"When a candidate accepts or declines the invite in Google Calendar, Reqcore reflects that response automatically",[18,7825,7827],{"id":7826},"cloud-vs-self-hosted","Cloud vs. self-hosted",[23,7829,7830,7842],{},[26,7831,7832],{},[29,7833,7834,7836,7839],{},[32,7835],{},[32,7837,7838],{},"reqcore.com (cloud)",[32,7840,7841],{},"Self-hosted",[39,7843,7844,7855,7866,7877],{},[29,7845,7846,7849,7852],{},[44,7847,7848],{},"OAuth app setup",[44,7850,7851],{},"Managed by reqcore — nothing to do",[44,7853,7854],{},"You create and manage your own Google OAuth app",[29,7856,7857,7860,7863],{},[44,7858,7859],{},"User connection",[44,7861,7862],{},"Click \"Connect\" → done",[44,7864,7865],{},"Admin sets up env vars first, then users click \"Connect\"",[29,7867,7868,7871,7874],{},[44,7869,7870],{},"Google API quota",[44,7872,7873],{},"Shared across all cloud users",[44,7875,7876],{},"Your own project — full quota",[29,7878,7879,7882,7885],{},[44,7880,7881],{},"Data residency",[44,7883,7884],{},"reqcore's infrastructure",[44,7886,7887],{},"Your infrastructure",[14,7889,7890,7891,7893,7894,7898],{},"If you are using ",[47,7892,6246],{},", skip to ",[137,7895,7897],{"href":7896},"#connecting-your-account","Connecting your account",". The steps below are only required for self-hosted instances.",[7900,7901],"hr",{},[18,7903,7905],{"id":7904},"self-hosted-setup","Self-hosted setup",[14,7907,7908,7909,3083,7911,835],{},"You need to create a Google OAuth 2.0 app and provide two environment variables: ",[53,7910,5545],{},[53,7912,5568],{},[14,7914,7915],{},"This is a one-time setup done by whoever administers the Reqcore server. Users do not need to touch Google Cloud.",[164,7917,7919],{"id":7918},"step-1-create-a-google-cloud-project","Step 1 — Create a Google Cloud project",[712,7921,7922,7930,7935,7945],{},[148,7923,7924,7925],{},"Go to ",[137,7926,7929],{"href":7927,"rel":7928},"https:\u002F\u002Fconsole.cloud.google.com",[141],"console.cloud.google.com",[148,7931,7932,7933],{},"Click the project dropdown in the top navigation bar, then click ",[47,7934,5999],{},[148,7936,7937,7938,7941,7942],{},"Give it a name (e.g. ",[53,7939,7940],{},"Reqcore",") and click ",[47,7943,7944],{},"Create",[148,7946,7947],{},"Make sure the new project is selected in the dropdown before continuing",[164,7949,7951],{"id":7950},"step-2-enable-the-google-calendar-api","Step 2 — Enable the Google Calendar API",[712,7953,7954,7960,7966],{},[148,7955,7956,7957],{},"In the left sidebar, go to ",[47,7958,7959],{},"APIs & Services → Library",[148,7961,7962,7963],{},"Search for ",[53,7964,7965],{},"Google Calendar API",[148,7967,7968,7969],{},"Click on it and click ",[47,7970,7971],{},"Enable",[164,7973,7975],{"id":7974},"step-3-configure-the-oauth-consent-screen","Step 3 — Configure the OAuth consent screen",[712,7977,7978,7983,8000,8004,8028],{},[148,7979,7924,7980],{},[47,7981,7982],{},"APIs & Services → OAuth consent screen",[148,7984,7985,7986],{},"Choose your user type:\n",[145,7987,7988,7994],{},[148,7989,7990,7993],{},[47,7991,7992],{},"Internal"," — only available if your organization uses Google Workspace. Recommended for company-internal deployments. No verification required.",[148,7995,7996,7999],{},[47,7997,7998],{},"External"," — for personal Gmail accounts or mixed deployments. Up to 100 test users can connect without Google verification (sufficient for most self-hosted deployments).",[148,8001,5996,8002],{},[47,8003,7944],{},[148,8005,8006,8007],{},"Fill in the required fields:\n",[145,8008,8009,8017,8023],{},[148,8010,8011,562,8014,8016],{},[47,8012,8013],{},"App name",[53,8015,7940],{}," (or your company name)",[148,8018,8019,8022],{},[47,8020,8021],{},"User support email",": your email address",[148,8024,8025,8022],{},[47,8026,8027],{},"Developer contact information",[148,8029,5996,8030],{},[47,8031,8032],{},"Save and Continue",[8034,8035,8037],"h4",{"id":8036},"add-scopes","Add scopes",[14,8039,8040,8041,8044],{},"On the Scopes page, click ",[47,8042,8043],{},"Add or Remove Scopes"," and add the following:",[23,8046,8047,8055],{},[26,8048,8049],{},[29,8050,8051,8053],{},[32,8052,570],{},[32,8054,2254],{},[39,8056,8057,8067],{},[29,8058,8059,8064],{},[44,8060,8061],{},[53,8062,8063],{},"https:\u002F\u002Fwww.googleapis.com\u002Fauth\u002Fcalendar.events",[44,8065,8066],{},"Create, update, and delete calendar events",[29,8068,8069,8074],{},[44,8070,8071],{},[53,8072,8073],{},"https:\u002F\u002Fwww.googleapis.com\u002Fauth\u002Fuserinfo.email",[44,8075,8076],{},"Display the connected account email in settings",[14,8078,5996,8079,8082,8083,835],{},[47,8080,8081],{},"Update",", then ",[47,8084,8032],{},[8034,8086,8088],{"id":8087},"add-test-users-external-apps-only","Add test users (External apps only)",[14,8090,8091,8092,8094],{},"If you chose ",[47,8093,7998],{}," user type, add each person who will use the calendar integration as a test user on the next screen. They must have a Google account (Gmail or Google Workspace).",[4946,8096,8097],{},[14,8098,8099,8102],{},[47,8100,8101],{},"Note:"," You can add up to 100 test users without Google verification. For larger deployments, submit your app for verification via the OAuth consent screen dashboard.",[14,8104,5996,8105,8082,8107,835],{},[47,8106,8032],{},[47,8108,8109],{},"Back to Dashboard",[164,8111,8113],{"id":8112},"step-4-create-oauth-20-credentials","Step 4 — Create OAuth 2.0 credentials",[712,8115,8116,8121,8126,8134,8139,8185,8189],{},[148,8117,7924,8118],{},[47,8119,8120],{},"APIs & Services → Credentials",[148,8122,5996,8123],{},[47,8124,8125],{},"Create Credentials → OAuth client ID",[148,8127,5051,8128,6278,8131],{},[47,8129,8130],{},"Application type",[53,8132,8133],{},"Web application",[148,8135,7937,8136,4575],{},[53,8137,8138],{},"Reqcore Web",[148,8140,8141,8142,8145,8146,8149,8150,8156,8159,8160,8163,8164,8166,8167,8170,8171,8177],{},"Under ",[47,8143,8144],{},"Authorized redirect URIs",", click ",[47,8147,8148],{},"Add URI"," and enter:",[169,8151,8154],{"className":8152,"code":8153,"language":347},[345],"https:\u002F\u002Fyour-domain.com\u002Fapi\u002Fcalendar\u002Fgoogle\u002Fcallback\n",[53,8155,8153],{"__ignoreMap":174},[8157,8158],"br",{},"Replace ",[53,8161,8162],{},"your-domain.com"," with the value of your ",[53,8165,5054],{}," environment variable. For example, if ",[53,8168,8169],{},"BETTER_AUTH_URL=https:\u002F\u002Frecruiting.mycompany.com",", the redirect URI is:",[169,8172,8175],{"className":8173,"code":8174,"language":347},[345],"https:\u002F\u002Frecruiting.mycompany.com\u002Fapi\u002Fcalendar\u002Fgoogle\u002Fcallback\n",[53,8176,8174],{"__ignoreMap":174},[4946,8178,8179],{},[14,8180,8181,8182,8184],{},"The redirect URI must exactly match what is set in ",[53,8183,5054],{},". An HTTP\u002FHTTPS mismatch or a trailing slash will cause the OAuth flow to fail.",[148,8186,5996,8187],{},[47,8188,7944],{},[148,8190,8191,8192,3083,8195,8198],{},"A dialog shows your ",[47,8193,8194],{},"Client ID",[47,8196,8197],{},"Client Secret"," — copy both now (you can retrieve them later from the Credentials page)",[164,8200,8202],{"id":8201},"step-5-set-environment-variables","Step 5 — Set environment variables",[14,8204,8205,8206,238,8208,8210],{},"Add the following to your environment configuration (",[53,8207,4931],{},[53,8209,4751],{},", or your hosting platform's secret manager):",[169,8212,8214],{"className":5724,"code":8213,"language":2283,"meta":174,"style":174},"GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com\nGOOGLE_CLIENT_SECRET=GOCSPX-your-client-secret\n",[53,8215,8216,8221],{"__ignoreMap":174},[178,8217,8218],{"class":180,"line":181},[178,8219,8220],{},"GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com\n",[178,8222,8223],{"class":180,"line":188},[178,8224,8225],{},"GOOGLE_CLIENT_SECRET=GOCSPX-your-client-secret\n",[14,8227,8228,8230,8231,8234,8235,8238,8239,2096],{},[47,8229,4739],{}," — add to the ",[53,8232,8233],{},"environment"," section of the ",[53,8236,8237],{},"app"," service in ",[53,8240,4751],{},[169,8242,8246],{"className":8243,"code":8244,"language":8245,"meta":174,"style":174},"language-yaml shiki shiki-themes github-light github-dark","environment:\n  GOOGLE_CLIENT_ID: \"your-client-id.apps.googleusercontent.com\"\n  GOOGLE_CLIENT_SECRET: \"GOCSPX-your-client-secret\"\n","yaml",[53,8247,8248,8255,8265],{"__ignoreMap":174},[178,8249,8250,8252],{"class":180,"line":181},[178,8251,8233],{"class":3130},[178,8253,8254],{"class":202},":\n",[178,8256,8257,8260,8262],{"class":180,"line":188},[178,8258,8259],{"class":3130},"  GOOGLE_CLIENT_ID",[178,8261,562],{"class":202},[178,8263,8264],{"class":610},"\"your-client-id.apps.googleusercontent.com\"\n",[178,8266,8267,8270,8272],{"class":180,"line":225},[178,8268,8269],{"class":3130},"  GOOGLE_CLIENT_SECRET",[178,8271,562],{"class":202},[178,8273,8274],{"class":610},"\"GOCSPX-your-client-secret\"\n",[14,8276,8277,8279,8280,8283],{},[47,8278,5955],{}," — add both variables in your service's ",[47,8281,8282],{},"Variables"," tab.",[164,8285,8287],{"id":8286},"step-6-restart-and-verify","Step 6 — Restart and verify",[14,8289,8290],{},"Restart the Reqcore server to pick up the new variables. Then:",[712,8292,8293,8298,8305],{},[148,8294,8295,8296],{},"Log in to your Reqcore instance and go to ",[47,8297,7817],{},[148,8299,8300,8301,8304],{},"The Google Calendar card should now show a ",[47,8302,8303],{},"Connect Google Calendar"," button instead of \"Not configured\"",[148,8306,8307],{},"The integration is ready for users to connect",[7900,8309],{},[18,8311,7897],{"id":8312},"connecting-your-account",[14,8314,8315],{},"Once the integration is configured (or if you're on reqcore.com), any team member can connect their Google Calendar:",[712,8317,8318,8322,8326,8332],{},[148,8319,7924,8320],{},[47,8321,7817],{},[148,8323,5996,8324],{},[47,8325,8303],{},[148,8327,8328,8329],{},"You'll be redirected to Google's sign-in and consent screen — select your account and click ",[47,8330,8331],{},"Allow",[148,8333,8334],{},"You'll return to the Integrations page with a confirmation message",[14,8336,8337],{},"After connecting:",[145,8339,8340,8343,8346,8349],{},[148,8341,8342],{},"New interviews you schedule will automatically create a Google Calendar event",[148,8344,8345],{},"The candidate and any co-interviewers are added as attendees",[148,8347,8348],{},"They'll receive a calendar invite and can accept or decline from their calendar",[148,8350,8351],{},"RSVP responses appear in Reqcore within seconds via webhook",[14,8353,8354,8355,835],{},"You can disconnect at any time from the same page. Tokens are encrypted at rest using your ",[53,8356,5258],{},[7900,8358],{},[18,8360,8362],{"id":8361},"troubleshooting","Troubleshooting",[164,8364,8366],{"id":8365},"not-configured-badge-on-the-integrations-page","\"Not configured\" badge on the integrations page",[14,8368,1954,8369,4623,8371,8373],{},[53,8370,5545],{},[53,8372,5568],{}," environment variables are missing or empty. Double-check the values and restart the server.",[164,8375,8377],{"id":8376},"redirect_uri_mismatch-error-from-google","\"redirect_uri_mismatch\" error from Google",[14,8379,8380,8381,2096],{},"The redirect URI registered in Google Cloud Console doesn't match what Reqcore is using. The URI Reqcore uses is derived from ",[53,8382,5054],{},[169,8384,8387],{"className":8385,"code":8386,"language":347},[345],"{BETTER_AUTH_URL}\u002Fapi\u002Fcalendar\u002Fgoogle\u002Fcallback\n",[53,8388,8386],{"__ignoreMap":174},[14,8390,7924,8391,8394,8395,8398],{},[47,8392,8393],{},"Google Cloud Console → APIs & Services → Credentials",", edit your OAuth client, and make sure the redirect URI matches exactly (including ",[53,8396,8397],{},"https:\u002F\u002F",", no trailing slash).",[164,8400,8402],{"id":8401},"access-blocked-this-apps-request-is-invalid-external-apps","\"Access blocked: This app's request is invalid\" (external apps)",[14,8404,8405,8406,835],{},"Your Google account is not on the test users list. Go to the OAuth consent screen in Google Cloud Console and add your email under ",[47,8407,8408],{},"Test users",[164,8410,8412],{"id":8411},"failed-to-obtain-oauth-tokens","\"Failed to obtain OAuth tokens\"",[14,8414,8415,8416,8419],{},"This happens if Google does not return a refresh token. This can occur if the user previously granted access and the consent screen was not shown again. Reqcore uses ",[53,8417,8418],{},"prompt: 'consent'"," to force re-consent, so this should not happen in normal use. If it does, the user can disconnect and reconnect to force a new consent prompt.",[164,8421,8423],{"id":8422},"two-way-sync-shows-pending-setup","Two-way sync shows \"Pending setup\"",[14,8425,8426,8427,8429],{},"The webhook registration happens automatically after connecting. If it shows \"Pending setup\" for more than a minute, the webhook may have failed to register — this is usually a network issue where Google cannot reach your server URL. Verify your instance is publicly accessible at the ",[53,8428,5054],{}," address.",[14,8431,8432],{},"Self-hosted instances on a private network (not publicly accessible) will not receive webhook push notifications. Calendar events will still be created, but RSVP responses won't sync back automatically.",[7900,8434],{},[18,8436,8438],{"id":8437},"related","Related",[145,8440,8441,8446,8451],{},[148,8442,8443,8445],{},[137,8444,785],{"href":784}," — Full environment variable reference",[148,8447,8448,8450],{},[137,8449,4745],{"href":5199}," — Self-hosted deployment guide",[148,8452,8453,8456],{},[137,8454,8455],{"href":6740},"Interview Scheduling"," — How interviews fit into the candidate workflow",[788,8458,8459],{},"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 .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":174,"searchDepth":188,"depth":188,"links":8461},[8462,8463,8464,8472,8473,8480],{"id":7407,"depth":188,"text":7806},{"id":7826,"depth":188,"text":7827},{"id":7904,"depth":188,"text":7905,"children":8465},[8466,8467,8468,8469,8470,8471],{"id":7918,"depth":225,"text":7919},{"id":7950,"depth":225,"text":7951},{"id":7974,"depth":225,"text":7975},{"id":8112,"depth":225,"text":8113},{"id":8201,"depth":225,"text":8202},{"id":8286,"depth":225,"text":8287},{"id":8312,"depth":188,"text":7897},{"id":8361,"depth":188,"text":8362,"children":8474},[8475,8476,8477,8478,8479],{"id":8365,"depth":225,"text":8366},{"id":8376,"depth":225,"text":8377},{"id":8401,"depth":225,"text":8402},{"id":8411,"depth":225,"text":8412},{"id":8422,"depth":225,"text":8423},{"id":8437,"depth":188,"text":8438},"Set up Google Calendar for automatic interview scheduling sync. Covers self-hosted OAuth app configuration and per-user connection.","calendar",{},{"title":5560,"description":8481},"docs\u002F3.features\u002F7.google-calendar","ggwAzmvWrUeP_R1kikcvKC2KhdveuEFfhjApqYpmf7I",{"id":8488,"title":7363,"body":8489,"description":8915,"extension":806,"icon":8916,"meta":8917,"navigation":403,"path":7362,"section":6768,"seo":8918,"stem":8919,"__hash__":8920},"docs\u002Fdocs\u002F3.features\u002F1.job-management.md",{"type":7,"value":8490,"toc":8905},[8491,8494,8497,8501,8504,8510,8574,8577,8581,8591,8623,8627,8716,8720,8729,8743,8754,8773,8776,8780,8783,8800,8802,8883,8886,8888],[10,8492,7363],{"id":8493},"job-management",[14,8495,8496],{},"Jobs are the foundation of Reqcore's hiring workflow. Each job belongs to an organization and moves through a defined status lifecycle.",[18,8498,8500],{"id":8499},"job-status-workflow","Job Status Workflow",[14,8502,8503],{},"Every job follows a four-stage lifecycle:",[169,8505,8508],{"className":8506,"code":8507,"language":347},[345],"Draft → Open → Closed → Archived\n",[53,8509,8507],{"__ignoreMap":174},[23,8511,8512,8525],{},[26,8513,8514],{},[29,8515,8516,8519,8522],{},[32,8517,8518],{},"Status",[32,8520,8521],{},"Meaning",[32,8523,8524],{},"Visible on Job Board",[39,8526,8527,8539,8550,8562],{},[29,8528,8529,8534,8537],{},[44,8530,8531],{},[47,8532,8533],{},"Draft",[44,8535,8536],{},"Job is being prepared, not yet published",[44,8538,2419],{},[29,8540,8541,8545,8548],{},[44,8542,8543],{},[47,8544,6557],{},[44,8546,8547],{},"Actively accepting applications",[44,8549,2452],{},[29,8551,8552,8557,8560],{},[44,8553,8554],{},[47,8555,8556],{},"Closed",[44,8558,8559],{},"No longer accepting applications",[44,8561,2419],{},[29,8563,8564,8569,8572],{},[44,8565,8566],{},[47,8567,8568],{},"Archived",[44,8570,8571],{},"Historical record, hidden from active views",[44,8573,2419],{},[14,8575,8576],{},"Status transitions are validated — you can only move forward through the workflow.",[18,8578,8580],{"id":8579},"creating-a-job","Creating a Job",[14,8582,8583,8584,8587,8588,8590],{},"Navigate to ",[47,8585,8586],{},"Jobs"," in the sidebar and click ",[47,8589,7311],{},". Required fields:",[23,8592,8593,8602],{},[26,8594,8595],{},[29,8596,8597,8600],{},[32,8598,8599],{},"Field",[32,8601,872],{},[39,8603,8604,8614],{},[29,8605,8606,8611],{},[44,8607,8608],{},[47,8609,8610],{},"Title",[44,8612,8613],{},"Job title displayed on the job board and in listings",[29,8615,8616,8620],{},[44,8617,8618],{},[47,8619,872],{},[44,8621,8622],{},"Full job description (Markdown supported)",[164,8624,8626],{"id":8625},"optional-fields","Optional Fields",[23,8628,8629,8637],{},[26,8630,8631],{},[29,8632,8633,8635],{},[32,8634,8599],{},[32,8636,2254],{},[39,8638,8639,8649,8659,8669,8679,8688,8697,8706],{},[29,8640,8641,8646],{},[44,8642,8643],{},[47,8644,8645],{},"Location",[44,8647,8648],{},"Office location or \"Remote\"",[29,8650,8651,8656],{},[44,8652,8653],{},[47,8654,8655],{},"Employment Type",[44,8657,8658],{},"Full-time, Part-time, Contract, Internship",[29,8660,8661,8666],{},[44,8662,8663],{},[47,8664,8665],{},"Salary Range",[44,8667,8668],{},"Min\u002Fmax salary for structured data",[29,8670,8671,8676],{},[44,8672,8673],{},[47,8674,8675],{},"Salary Currency",[44,8677,8678],{},"Currency code (USD, EUR, GBP, etc.)",[29,8680,8681,8686],{},[44,8682,8683],{},[47,8684,8685],{},"Salary Unit",[44,8687,1046],{},[29,8689,8690,8695],{},[44,8691,8692],{},[47,8693,8694],{},"Remote Status",[44,8696,1060],{},[29,8698,8699,8704],{},[44,8700,8701],{},[47,8702,8703],{},"Valid Through",[44,8705,1075],{},[29,8707,8708,8713],{},[44,8709,8710],{},[47,8711,8712],{},"Custom Slug",[44,8714,8715],{},"URL path for the public job page",[18,8717,8719],{"id":8718},"seo-optimized-job-pages","SEO-Optimized Job Pages",[14,8721,8722,8723,8725,8726,2096],{},"When a job is set to ",[47,8724,6557],{},", it gets a public page at ",[53,8727,8728],{},"\u002Fjobs\u002F{slug}",[145,8730,8731,8737,8740],{},[148,8732,8733,8736],{},[47,8734,8735],{},"Slug"," is auto-generated from the job title + short UUID on creation",[148,8738,8739],{},"Recruiters can set a custom slug for more memorable URLs",[148,8741,8742],{},"Slugs are regenerated when the title or custom slug changes",[14,8744,8745,8746,8753],{},"Each public job page includes ",[47,8747,8748,8749,8752],{},"JSON-LD ",[53,8750,8751],{},"JobPosting"," structured data"," with:",[145,8755,8756,8759,8762,8765,8768],{},[148,8757,8758],{},"Title, description, location",[148,8760,8761],{},"Salary range and currency",[148,8763,8764],{},"Remote status and employment type",[148,8766,8767],{},"Hiring organization name",[148,8769,8770,8771,4575],{},"Application deadline (",[53,8772,1067],{},[14,8774,8775],{},"This structured data helps the job appear in Google for Jobs and other job search engines.",[18,8777,8779],{"id":8778},"job-sub-pages","Job Sub-Pages",[14,8781,8782],{},"Each job has three tabs in the dashboard:",[712,8784,8785,8790,8795],{},[148,8786,8787,8789],{},[47,8788,2167],{}," — Job details, status management, and editing",[148,8791,8792,8794],{},[47,8793,6876],{}," — Kanban board showing candidates by stage",[148,8796,8797,8799],{},[47,8798,6496],{}," — Custom questions and the shareable application link",[18,8801,6635],{"id":6634},[23,8803,8804,8814],{},[26,8805,8806],{},[29,8807,8808,8810,8812],{},[32,8809,6644],{},[32,8811,6647],{},[32,8813,872],{},[39,8815,8816,8830,8843,8857,8870],{},[29,8817,8818,8822,8827],{},[44,8819,8820],{},[53,8821,6658],{},[44,8823,8824],{},[53,8825,8826],{},"\u002Fapi\u002Fjobs",[44,8828,8829],{},"List all jobs (org-scoped)",[29,8831,8832,8836,8840],{},[44,8833,8834],{},[53,8835,6673],{},[44,8837,8838],{},[53,8839,8826],{},[44,8841,8842],{},"Create a new job",[29,8844,8845,8849,8854],{},[44,8846,8847],{},[53,8848,6658],{},[44,8850,8851],{},[53,8852,8853],{},"\u002Fapi\u002Fjobs\u002F:id",[44,8855,8856],{},"Get job details",[29,8858,8859,8863,8867],{},[44,8860,8861],{},[53,8862,6687],{},[44,8864,8865],{},[53,8866,8853],{},[44,8868,8869],{},"Update a job",[29,8871,8872,8876,8880],{},[44,8873,8874],{},[53,8875,6702],{},[44,8877,8878],{},[53,8879,8853],{},[44,8881,8882],{},"Delete a job",[14,8884,8885],{},"All endpoints require authentication and scope data to the user's active organization.",[18,8887,763],{"id":762},[145,8889,8890,8895,8900],{},[148,8891,8892,8894],{},[137,8893,6741],{"href":6740}," — Track candidates through hiring stages",[148,8896,8897,8899],{},[137,8898,6342],{"href":6767}," — Add custom questions to your jobs",[148,8901,8902,8904],{},[137,8903,6734],{"href":6733}," — How the public-facing job listing works",{"title":174,"searchDepth":188,"depth":188,"links":8906},[8907,8908,8911,8912,8913,8914],{"id":8499,"depth":188,"text":8500},{"id":8579,"depth":188,"text":8580,"children":8909},[8910],{"id":8625,"depth":225,"text":8626},{"id":8718,"depth":188,"text":8719},{"id":8778,"depth":188,"text":8779},{"id":6634,"depth":188,"text":6635},{"id":762,"depth":188,"text":763},"Create, edit, and track jobs through their lifecycle in Reqcore. Covers status workflows, SEO fields, and custom slugs.","briefcase",{},{"title":7363,"description":8915},"docs\u002F3.features\u002F1.job-management","9w-ooa0pQTlCi8C8CucMhUDTCMcqWaPX7NwjWUFz0KE",{"id":8922,"title":6734,"body":8923,"description":9318,"extension":806,"icon":9319,"meta":9320,"navigation":403,"path":6733,"section":6768,"seo":9321,"stem":9322,"__hash__":9323},"docs\u002Fdocs\u002F3.features\u002F4.public-job-board.md",{"type":7,"value":8924,"toc":9299},[8925,8928,8935,8937,8948,8954,8957,8971,8978,8981,9001,9005,9008,9012,9018,9127,9130,9134,9148,9152,9164,9168,9193,9197,9208,9210,9266,9270,9277,9279,9296],[10,8926,6734],{"id":8927},"public-job-board",[14,8929,8930,8931,8934],{},"Reqcore includes a public-facing job board at ",[53,8932,8933],{},"\u002Fjobs"," where applicants can browse open positions and submit applications — no login required.",[18,8936,7408],{"id":7407},[14,8938,8939,8940,8942,8943,4623,8945,8947],{},"When a recruiter changes a job's status to ",[47,8941,6557],{},", the job automatically appears on the public board. When the status changes to ",[47,8944,8556],{},[47,8946,8568],{},", the job disappears from the board.",[164,8949,8951,8952,4575],{"id":8950},"job-listing-page-jobs","Job Listing Page (",[53,8953,8933],{},[14,8955,8956],{},"The listing page shows all open jobs across all organizations, featuring:",[145,8958,8959,8962,8965,8968],{},[148,8960,8961],{},"Job title, location, employment type",[148,8963,8964],{},"Organization name",[148,8966,8967],{},"Posted date",[148,8969,8970],{},"Link to the full job detail page",[164,8972,8974,8975,4575],{"id":8973},"job-detail-page-jobsslug","Job Detail Page (",[53,8976,8977],{},"\u002Fjobs\u002F:slug",[14,8979,8980],{},"Each open job has a dedicated page with:",[145,8982,8983,8986,8989,8992,8995],{},[148,8984,8985],{},"Full job description (rendered from Markdown)",[148,8987,8988],{},"Location and employment type",[148,8990,8991],{},"Salary range (if provided)",[148,8993,8994],{},"Remote status",[148,8996,8997,9000],{},[47,8998,8999],{},"Apply Now"," button linking to the application form",[18,9002,9004],{"id":9003},"seo-optimization","SEO Optimization",[14,9006,9007],{},"Public job pages are optimized for search engines and job aggregators:",[164,9009,9011],{"id":9010},"structured-data-json-ld","Structured Data (JSON-LD)",[14,9013,9014,9015,9017],{},"Each job detail page includes a ",[53,9016,8751],{}," schema with:",[169,9019,9022],{"className":9020,"code":9021,"language":1648,"meta":174,"style":174},"language-json shiki shiki-themes github-light github-dark","{\n  \"@type\": \"JobPosting\",\n  \"title\": \"Senior Software Engineer\",\n  \"description\": \"...\",\n  \"employmentType\": \"FULL_TIME\",\n  \"jobLocation\": { ... },\n  \"baseSalary\": { ... },\n  \"hiringOrganization\": { ... },\n  \"validThrough\": \"2026-06-01\"\n}\n",[53,9023,9024,9028,9040,9052,9064,9076,9091,9102,9113,9123],{"__ignoreMap":174},[178,9025,9026],{"class":180,"line":181},[178,9027,605],{"class":202},[178,9029,9030,9033,9035,9038],{"class":180,"line":188},[178,9031,9032],{"class":234},"  \"@type\"",[178,9034,562],{"class":202},[178,9036,9037],{"class":610},"\"JobPosting\"",[178,9039,619],{"class":202},[178,9041,9042,9045,9047,9050],{"class":180,"line":225},[178,9043,9044],{"class":234},"  \"title\"",[178,9046,562],{"class":202},[178,9048,9049],{"class":610},"\"Senior Software Engineer\"",[178,9051,619],{"class":202},[178,9053,9054,9057,9059,9062],{"class":180,"line":259},[178,9055,9056],{"class":234},"  \"description\"",[178,9058,562],{"class":202},[178,9060,9061],{"class":610},"\"...\"",[178,9063,619],{"class":202},[178,9065,9066,9069,9071,9074],{"class":180,"line":273},[178,9067,9068],{"class":234},"  \"employmentType\"",[178,9070,562],{"class":202},[178,9072,9073],{"class":610},"\"FULL_TIME\"",[178,9075,619],{"class":202},[178,9077,9078,9081,9084,9088],{"class":180,"line":279},[178,9079,9080],{"class":234},"  \"jobLocation\"",[178,9082,9083],{"class":202},": { ",[178,9085,9087],{"class":9086},"s7hpK","...",[178,9089,9090],{"class":202}," },\n",[178,9092,9093,9096,9098,9100],{"class":180,"line":658},[178,9094,9095],{"class":234},"  \"baseSalary\"",[178,9097,9083],{"class":202},[178,9099,9087],{"class":9086},[178,9101,9090],{"class":202},[178,9103,9104,9107,9109,9111],{"class":180,"line":671},[178,9105,9106],{"class":234},"  \"hiringOrganization\"",[178,9108,9083],{"class":202},[178,9110,9087],{"class":9086},[178,9112,9090],{"class":202},[178,9114,9115,9118,9120],{"class":180,"line":3219},[178,9116,9117],{"class":234},"  \"validThrough\"",[178,9119,562],{"class":202},[178,9121,9122],{"class":610},"\"2026-06-01\"\n",[178,9124,9125],{"class":180,"line":3225},[178,9126,674],{"class":202},[14,9128,9129],{},"This structured data powers Google for Jobs and other job search engines.",[164,9131,9133],{"id":9132},"url-slugs","URL Slugs",[145,9135,9136,9142,9145],{},[148,9137,9138,9139,4575],{},"Slugs are auto-generated from the job title + short UUID (e.g., ",[53,9140,9141],{},"\u002Fjobs\u002Fsenior-engineer-a1b2c3d4",[148,9143,9144],{},"Recruiters can set custom slugs for more memorable URLs",[148,9146,9147],{},"Slugs are URL-safe and human-readable",[164,9149,9151],{"id":9150},"route-rules","Route Rules",[145,9153,9154,9161],{},[148,9155,9156,9157,9160],{},"Job listing and detail pages use ",[47,9158,9159],{},"ISR (Incremental Static Regeneration)"," with a 1-hour revalidation window",[148,9162,9163],{},"This balances SEO performance with fresh content",[18,9165,9167],{"id":9166},"public-application-flow","Public Application Flow",[712,9169,9170,9175,9184,9187],{},[148,9171,9172,9173],{},"Applicant visits ",[53,9174,8977],{},[148,9176,9177,9178,9180,9181],{},"Clicks ",[47,9179,8999],{}," → redirected to ",[53,9182,9183],{},"\u002Fjobs\u002F:slug\u002Fapply",[148,9185,9186],{},"Fills in the application form (custom questions + file uploads)",[148,9188,9189,9190],{},"Submits → redirected to ",[53,9191,9192],{},"\u002Fjobs\u002F:slug\u002Fconfirmation",[164,9194,9196],{"id":9195},"anti-spam-protection","Anti-Spam Protection",[145,9198,9199,9202,9205],{},[148,9200,9201],{},"Honeypot field on the submission form",[148,9203,9204],{},"IP-based rate limiting on the public apply endpoint",[148,9206,9207],{},"No CAPTCHA required (for better UX)",[18,9209,6635],{"id":6634},[23,9211,9212,9222],{},[26,9213,9214],{},[29,9215,9216,9218,9220],{},[32,9217,6644],{},[32,9219,6647],{},[32,9221,872],{},[39,9223,9224,9238,9252],{},[29,9225,9226,9230,9235],{},[44,9227,9228],{},[53,9229,6658],{},[44,9231,9232],{},[53,9233,9234],{},"\u002Fapi\u002Fpublic\u002Fjobs",[44,9236,9237],{},"List all open jobs (no auth)",[29,9239,9240,9244,9249],{},[44,9241,9242],{},[53,9243,6658],{},[44,9245,9246],{},[53,9247,9248],{},"\u002Fapi\u002Fpublic\u002Fjobs\u002F:slug",[44,9250,9251],{},"Job detail with custom questions (no auth)",[29,9253,9254,9258,9263],{},[44,9255,9256],{},[53,9257,6673],{},[44,9259,9260],{},[53,9261,9262],{},"\u002Fapi\u002Fpublic\u002Fjobs\u002F:slug\u002Fapply",[44,9264,9265],{},"Submit an application (no auth, rate-limited)",[18,9267,9269],{"id":9268},"dynamic-sitemap","Dynamic Sitemap",[14,9271,9272,9273,9276],{},"Open jobs are automatically added to the sitemap via a dynamic source endpoint at ",[53,9274,9275],{},"\u002Fapi\u002F__sitemap__\u002Furls",". This ensures search engines discover new job postings as they are published.",[18,9278,763],{"id":762},[145,9280,9281,9286,9291],{},[148,9282,9283,9285],{},[137,9284,6342],{"href":6767}," — Configure custom questions for your jobs",[148,9287,9288,9290],{},[137,9289,7363],{"href":7362}," — Manage job lifecycle and SEO fields",[148,9292,9293,9295],{},[137,9294,771],{"href":770}," — How public and authenticated routes are separated",[788,9297,9298],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}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":174,"searchDepth":188,"depth":188,"links":9300},[9301,9307,9312,9315,9316,9317],{"id":7407,"depth":188,"text":7408,"children":9302},[9303,9305],{"id":8950,"depth":225,"text":9304},"Job Listing Page (\u002Fjobs)",{"id":8973,"depth":225,"text":9306},"Job Detail Page (\u002Fjobs\u002F:slug)",{"id":9003,"depth":188,"text":9004,"children":9308},[9309,9310,9311],{"id":9010,"depth":225,"text":9011},{"id":9132,"depth":225,"text":9133},{"id":9150,"depth":225,"text":9151},{"id":9166,"depth":188,"text":9167,"children":9313},[9314],{"id":9195,"depth":225,"text":9196},{"id":6634,"depth":188,"text":6635},{"id":9268,"depth":188,"text":9269},{"id":762,"depth":188,"text":763},"SEO-friendly public job listings where applicants can browse and apply to open positions. Covers routing, structured data, and customization.","globe",{},{"title":6734,"description":9318},"docs\u002F3.features\u002F4.public-job-board","keAbjbjFquACKcbgOPXz1dMejIqtgKlYweNjQSIOKIY",{"id":9325,"title":9326,"body":9327,"description":9878,"extension":806,"icon":9879,"meta":9880,"navigation":403,"path":9881,"section":4204,"seo":9882,"stem":9883,"__hash__":9884},"docs\u002Fdocs\u002F1.getting-started\u002F3.configuration.md","Configuration",{"type":7,"value":9328,"toc":9864},[9329,9332,9335,9337,9339,9458,9460,9591,9595,9601,9624,9658,9672,9676,9680,9683,9713,9717,9720,9729,9732,9734,9740,9760,9764,9775,9839,9842,9844,9861],[10,9330,9326],{"id":9331},"configuration",[14,9333,9334],{},"Reqcore uses environment variables for all configuration. Variables are validated at startup using Zod — if any required variable is missing or malformed, the server fails immediately with a clear error message.",[18,9336,785],{"id":4045},[164,9338,5219],{"id":5218},[23,9340,9341,9351],{},[26,9342,9343],{},[29,9344,9345,9347,9349],{},[32,9346,5231],{},[32,9348,872],{},[32,9350,3435],{},[39,9352,9353,9366,9380,9392,9406,9420,9433,9445],{},[29,9354,9355,9359,9361],{},[44,9356,9357],{},[53,9358,5244],{},[44,9360,5251],{},[44,9362,9363],{},[53,9364,9365],{},"postgresql:\u002F\u002Fuser:pass@localhost:5432\u002Freqcore",[29,9367,9368,9372,9375],{},[44,9369,9370],{},[53,9371,5258],{},[44,9373,9374],{},"Session signing secret (min 32 chars)",[44,9376,9377,9378],{},"Generated by ",[53,9379,728],{},[29,9381,9382,9386,9388],{},[44,9383,9384],{},[53,9385,5054],{},[44,9387,5278],{},[44,9389,9390],{},[53,9391,4326],{},[29,9393,9394,9398,9401],{},[44,9395,9396],{},[53,9397,5285],{},[44,9399,9400],{},"S3-compatible storage endpoint",[44,9402,9403],{},[53,9404,9405],{},"http:\u002F\u002Flocalhost:9000",[29,9407,9408,9412,9415],{},[44,9409,9410],{},[53,9411,5299],{},[44,9413,9414],{},"Storage access key",[44,9416,9417],{},[53,9418,9419],{},"minioadmin",[29,9421,9422,9426,9429],{},[44,9423,9424],{},[53,9425,5313],{},[44,9427,9428],{},"Storage secret key",[44,9430,9431],{},[53,9432,9419],{},[29,9434,9435,9439,9441],{},[44,9436,9437],{},[53,9438,5327],{},[44,9440,5334],{},[44,9442,9443],{},[53,9444,4876],{},[29,9446,9447,9451,9454],{},[44,9448,9449],{},[53,9450,5341],{},[44,9452,9453],{},"Storage region",[44,9455,9456],{},[53,9457,5351],{},[164,9459,5355],{"id":5354},[23,9461,9462,9472],{},[26,9463,9464],{},[29,9465,9466,9468,9470],{},[32,9467,5231],{},[32,9469,872],{},[32,9471,5371],{},[39,9473,9474,9487,9500,9513,9526,9539,9552,9565,9578],{},[29,9475,9476,9480,9483],{},[44,9477,9478],{},[53,9479,5382],{},[44,9481,9482],{},"Use path-style S3 URLs (required for MinIO)",[44,9484,9485],{},[53,9486,5391],{},[29,9488,9489,9493,9496],{},[44,9490,9491],{},[53,9492,5428],{},[44,9494,9495],{},"Public site URL for SEO",[44,9497,9498],{},[53,9499,5437],{},[29,9501,9502,9506,9509],{},[44,9503,9504],{},[53,9505,5467],{},[44,9507,9508],{},"Show read-only demo banner for this org",[44,9510,9511],{},[5475,9512,5477],{},[29,9514,9515,9519,9522],{},[44,9516,9517],{},[53,9518,5487],{},[44,9520,9521],{},"Prefill sign-in with demo email",[44,9523,9524],{},[53,9525,5496],{},[29,9527,9528,9532,9535],{},[44,9529,9530],{},[53,9531,5506],{},[44,9533,9534],{},"Prefill sign-in with demo password",[44,9536,9537],{},[53,9538,5515],{},[29,9540,9541,9545,9548],{},[44,9542,9543],{},[53,9544,5586],{},[44,9546,9547],{},"GitHub PAT for in-app feedback",[44,9549,9550],{},[5475,9551,5477],{},[29,9553,9554,9558,9561],{},[44,9555,9556],{},[53,9557,5604],{},[44,9559,9560],{},"GitHub repo for feedback issues",[44,9562,9563],{},[5475,9564,5477],{},[29,9566,9567,9571,9574],{},[44,9568,9569],{},[53,9570,5625],{},[44,9572,9573],{},"Giscus repo node ID for comments",[44,9575,9576],{},[5475,9577,5477],{},[29,9579,9580,9584,9587],{},[44,9581,9582],{},[53,9583,5643],{},[44,9585,9586],{},"Giscus category node ID for comments",[44,9588,9589],{},[5475,9590,5477],{},[18,9592,9594],{"id":9593},"environment-validation","Environment Validation",[14,9596,9597,9598,9600],{},"All environment variables are validated in ",[53,9599,2329],{}," using Zod schemas. This ensures:",[145,9602,9603,9609,9618],{},[148,9604,9605,9608],{},[47,9606,9607],{},"Fail-fast",": Missing or invalid variables crash the server at startup, not at runtime",[148,9610,9611,9614,9615,9617],{},[47,9612,9613],{},"Type safety",": Every variable is typed and available via the ",[53,9616,2283],{}," utility",[148,9619,9620,9623],{},[47,9621,9622],{},"No stale values",": The validation catches old domain names or malformed URLs",[169,9625,9627],{"className":171,"code":9626,"language":173,"meta":174,"style":174},"\u002F\u002F Access environment variables in server code:\nimport { env } from '~\u002Fserver\u002Futils\u002Fenv'\n\n\u002F\u002F env.DATABASE_URL — always a valid string\n\u002F\u002F env.S3_BUCKET — always a valid string\n",[53,9628,9629,9634,9644,9648,9653],{"__ignoreMap":174},[178,9630,9631],{"class":180,"line":181},[178,9632,9633],{"class":184},"\u002F\u002F Access environment variables in server code:\n",[178,9635,9636,9638,9640,9642],{"class":180,"line":188},[178,9637,3155],{"class":191},[178,9639,4062],{"class":202},[178,9641,1797],{"class":191},[178,9643,4067],{"class":610},[178,9645,9646],{"class":180,"line":225},[178,9647,404],{"emptyLinePlaceholder":403},[178,9649,9650],{"class":180,"line":259},[178,9651,9652],{"class":184},"\u002F\u002F env.DATABASE_URL — always a valid string\n",[178,9654,9655],{"class":180,"line":273},[178,9656,9657],{"class":184},"\u002F\u002F env.S3_BUCKET — always a valid string\n",[4946,9659,9660],{},[14,9661,9662,9665,9666,9668,9669,9671],{},[47,9663,9664],{},"Important",": Never access ",[53,9667,2333],{}," directly in server code. Always use the validated ",[53,9670,2283],{}," utility.",[18,9673,9675],{"id":9674},"s3-storage-configuration","S3 Storage Configuration",[164,9677,9679],{"id":9678},"local-development-minio","Local Development (MinIO)",[14,9681,9682],{},"The default Docker Compose setup includes MinIO with path-style URLs:",[169,9684,9686],{"className":5724,"code":9685,"language":2283,"meta":174,"style":174},"S3_ENDPOINT=http:\u002F\u002Flocalhost:9000\nS3_ACCESS_KEY=minioadmin\nS3_SECRET_KEY=minioadmin\nS3_BUCKET=reqcore\nS3_REGION=us-east-1\nS3_FORCE_PATH_STYLE=true\n",[53,9687,9688,9692,9696,9701,9705,9709],{"__ignoreMap":174},[178,9689,9690],{"class":180,"line":181},[178,9691,5770],{},[178,9693,9694],{"class":180,"line":188},[178,9695,5775],{},[178,9697,9698],{"class":180,"line":225},[178,9699,9700],{},"S3_SECRET_KEY=minioadmin\n",[178,9702,9703],{"class":180,"line":259},[178,9704,5785],{},[178,9706,9707],{"class":180,"line":273},[178,9708,5790],{},[178,9710,9711],{"class":180,"line":279},[178,9712,5795],{},[164,9714,9716],{"id":9715},"production-railway-buckets-aws-s3","Production (Railway Buckets \u002F AWS S3)",[14,9718,9719],{},"For production deployments using Railway Buckets or AWS S3, use virtual-hosted-style URLs:",[169,9721,9723],{"className":5724,"code":9722,"language":2283,"meta":174,"style":174},"S3_FORCE_PATH_STYLE=false\n",[53,9724,9725],{"__ignoreMap":174},[178,9726,9727],{"class":180,"line":181},[178,9728,9722],{},[14,9730,9731],{},"Railway injects storage credentials automatically via template variables.",[18,9733,49],{"id":132},[14,9735,135,9736,9739],{},[137,9737,142],{"href":139,"rel":9738},[141]," with the organization plugin for multi-tenancy. The key configuration:",[145,9741,9742,9749],{},[148,9743,9744,9746,9747,835],{},[53,9745,5258],{}," — Must be at least 32 characters. Generated automatically by ",[53,9748,728],{},[148,9750,9751,9753,9754,9756,9757,9759],{},[53,9752,5054],{}," — Must match the public URL where users access the app. For local dev: ",[53,9755,4326],{},". For production: your domain (e.g., ",[53,9758,5437],{},").",[18,9761,9763],{"id":9762},"seo-configuration","SEO Configuration",[14,9765,9766,9767,9770,9771,9774],{},"SEO settings are defined in ",[53,9768,9769],{},"nuxt.config.ts"," under the ",[53,9772,9773],{},"site"," key:",[169,9776,9778],{"className":171,"code":9777,"language":173,"meta":174,"style":174},"site: {\n  url: 'https:\u002F\u002Freqcore.com',\n  name: 'Reqcore',\n  description: 'Open-source applicant tracking system...',\n  defaultLocale: 'en',\n}\n",[53,9779,9780,9787,9799,9811,9823,9835],{"__ignoreMap":174},[178,9781,9782,9784],{"class":180,"line":181},[178,9783,9773],{"class":198},[178,9785,9786],{"class":202},": {\n",[178,9788,9789,9792,9794,9797],{"class":180,"line":188},[178,9790,9791],{"class":198},"  url",[178,9793,562],{"class":202},[178,9795,9796],{"class":610},"'https:\u002F\u002Freqcore.com'",[178,9798,619],{"class":202},[178,9800,9801,9804,9806,9809],{"class":180,"line":225},[178,9802,9803],{"class":198},"  name",[178,9805,562],{"class":202},[178,9807,9808],{"class":610},"'Reqcore'",[178,9810,619],{"class":202},[178,9812,9813,9816,9818,9821],{"class":180,"line":259},[178,9814,9815],{"class":198},"  description",[178,9817,562],{"class":202},[178,9819,9820],{"class":610},"'Open-source applicant tracking system...'",[178,9822,619],{"class":202},[178,9824,9825,9828,9830,9833],{"class":180,"line":273},[178,9826,9827],{"class":198},"  defaultLocale",[178,9829,562],{"class":202},[178,9831,9832],{"class":610},"'en'",[178,9834,619],{"class":202},[178,9836,9837],{"class":180,"line":279},[178,9838,674],{"class":202},[14,9840,9841],{},"These values are shared across all SEO modules (Sitemap, Robots, Schema.org).",[18,9843,763],{"id":762},[145,9845,9846,9851,9856],{},[148,9847,9848,9850],{},[137,9849,4765],{"href":7374}," — Start using Reqcore",[148,9852,9853,9855],{},[137,9854,4745],{"href":5199}," — Production-ready local deployment",[148,9857,9858,9860],{},[137,9859,5174],{"href":5173}," — Deploy to Railway cloud",[788,9862,9863],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":174,"searchDepth":188,"depth":188,"links":9865},[9866,9870,9871,9875,9876,9877],{"id":4045,"depth":188,"text":785,"children":9867},[9868,9869],{"id":5218,"depth":225,"text":5219},{"id":5354,"depth":225,"text":5355},{"id":9593,"depth":188,"text":9594},{"id":9674,"depth":188,"text":9675,"children":9872},[9873,9874],{"id":9678,"depth":225,"text":9679},{"id":9715,"depth":225,"text":9716},{"id":132,"depth":188,"text":49},{"id":9762,"depth":188,"text":9763},{"id":762,"depth":188,"text":763},"Environment variables, runtime config, and application settings for Reqcore. Covers database, storage, authentication, and SEO configuration.","settings",{},"\u002Fdocs\u002Fgetting-started\u002Fconfiguration",{"title":9326,"description":9878},"docs\u002F1.getting-started\u002F3.configuration","wyKVlz8WHuTdSKNUjxgkfnzV8DDWgm86uYZJkGdpVvM",{"id":9886,"title":9887,"body":9888,"description":10496,"extension":806,"icon":7487,"meta":10497,"navigation":403,"path":10498,"section":4204,"seo":10499,"stem":10500,"__hash__":10501},"docs\u002Fdocs\u002F1.getting-started\u002F2.installation.md","Installation",{"type":7,"value":9889,"toc":10478},[9890,9893,9896,9898,9985,9989,10047,10051,10070,10084,10088,10094,10102,10105,10123,10135,10139,10142,10157,10160,10174,10177,10203,10207,10218,10222,10235,10242,10246,10249,10331,10335,10387,10389,10393,10400,10404,10413,10428,10432,10435,10451,10461,10463,10475],[10,9891,9887],{"id":9892},"installation",[14,9894,9895],{},"This guide walks you through setting up Reqcore on your local machine using Docker Compose. The entire process takes about 5 minutes.",[18,9897,4172],{"id":4171},[23,9899,9900,9913],{},[26,9901,9902],{},[29,9903,9904,9907,9910],{},[32,9905,9906],{},"Requirement",[32,9908,9909],{},"Minimum Version",[32,9911,9912],{},"Check Command",[39,9914,9915,9929,9943,9957,9971],{},[29,9916,9917,9921,9924],{},[44,9918,9919],{},[47,9920,4189],{},[44,9922,9923],{},"20.x+",[44,9925,9926],{},[53,9927,9928],{},"docker --version",[29,9930,9931,9935,9938],{},[44,9932,9933],{},[47,9934,4739],{},[44,9936,9937],{},"v2+",[44,9939,9940],{},[53,9941,9942],{},"docker compose version",[29,9944,9945,9949,9952],{},[44,9946,9947],{},[47,9948,4181],{},[44,9950,9951],{},"18.20+ (LTS 20+ recommended)",[44,9953,9954],{},[53,9955,9956],{},"node --version",[29,9958,9959,9963,9966],{},[44,9960,9961],{},[47,9962,1839],{},[44,9964,9965],{},"9+",[44,9967,9968],{},[53,9969,9970],{},"npm --version",[29,9972,9973,9977,9980],{},[44,9974,9975],{},[47,9976,4197],{},[44,9978,9979],{},"Any recent version",[44,9981,9982],{},[53,9983,9984],{},"git --version",[164,9986,9988],{"id":9987},"installing-docker","Installing Docker",[23,9990,9991,10001],{},[26,9992,9993],{},[29,9994,9995,9998],{},[32,9996,9997],{},"OS",[32,9999,10000],{},"Instructions",[39,10002,10003,10017,10031],{},[29,10004,10005,10010],{},[44,10006,10007],{},[47,10008,10009],{},"macOS",[44,10011,10012],{},[137,10013,10016],{"href":10014,"rel":10015},"https:\u002F\u002Fwww.docker.com\u002Fproducts\u002Fdocker-desktop\u002F",[141],"Download Docker Desktop for Mac",[29,10018,10019,10024],{},[44,10020,10021],{},[47,10022,10023],{},"Windows",[44,10025,10026,10030],{},[137,10027,10029],{"href":10014,"rel":10028},[141],"Download Docker Desktop for Windows"," — use Git Bash for all commands",[29,10032,10033,10038],{},[44,10034,10035],{},[47,10036,10037],{},"Linux",[44,10039,10040,10041,10046],{},"Follow the ",[137,10042,10045],{"href":10043,"rel":10044},"https:\u002F\u002Fdocs.docker.com\u002Fengine\u002Finstall\u002F",[141],"Docker Engine install guide"," for your distro",[18,10048,10050],{"id":10049},"step-1-clone-the-repository","Step 1 — Clone the Repository",[169,10052,10054],{"className":1825,"code":10053,"language":1827,"meta":174,"style":174},"git clone https:\u002F\u002Fgithub.com\u002Freqcore-inc\u002Freqcore.git\ncd reqcore\n",[53,10055,10056,10064],{"__ignoreMap":174},[178,10057,10058,10060,10062],{"class":180,"line":181},[178,10059,4219],{"class":198},[178,10061,4222],{"class":610},[178,10063,4784],{"class":610},[178,10065,10066,10068],{"class":180,"line":188},[178,10067,4230],{"class":234},[178,10069,4233],{"class":610},[4946,10071,10072],{},[14,10073,10074,10077,10078,10083],{},[47,10075,10076],{},"No Git?"," ",[137,10079,10082],{"href":10080,"rel":10081},"https:\u002F\u002Fgithub.com\u002Freqcore-inc\u002Freqcore\u002Farchive\u002Frefs\u002Fheads\u002Fmain.zip",[141],"Download a ZIP"," and unzip manually.",[18,10085,10087],{"id":10086},"step-2-generate-environment-variables","Step 2 — Generate Environment Variables",[14,10089,10090,10091,10093],{},"Run the setup script to create a ",[53,10092,4931],{}," file with random passwords and secrets:",[169,10095,10096],{"className":1825,"code":4263,"language":1827,"meta":174,"style":174},[53,10097,10098],{"__ignoreMap":174},[178,10099,10100],{"class":180,"line":181},[178,10101,4263],{"class":198},[14,10103,10104],{},"This generates cryptographically secure values for:",[145,10106,10107,10112,10118],{},[148,10108,10109,10111],{},[53,10110,5258],{}," — session signing key",[148,10113,10114,10117],{},[53,10115,10116],{},"POSTGRES_PASSWORD"," — database password",[148,10119,10120,10122],{},[53,10121,4937],{}," — object storage password",[4946,10124,10125],{},[14,10126,10127,10130,10131,10134],{},[47,10128,10129],{},"Windows users:"," Run ",[53,10132,10133],{},"bash setup.sh"," from Git Bash.",[18,10136,10138],{"id":10137},"step-3-start-infrastructure-services","Step 3 — Start Infrastructure Services",[14,10140,10141],{},"Start PostgreSQL, MinIO (S3-compatible object storage), and Adminer (database GUI):",[169,10143,10145],{"className":1825,"code":10144,"language":1827,"meta":174,"style":174},"docker compose up -d\n",[53,10146,10147],{"__ignoreMap":174},[178,10148,10149,10151,10153,10155],{"class":180,"line":181},[178,10150,4277],{"class":198},[178,10152,4280],{"class":610},[178,10154,4283],{"class":610},[178,10156,4286],{"class":234},[14,10158,10159],{},"Verify everything is running:",[169,10161,10163],{"className":1825,"code":10162,"language":1827,"meta":174,"style":174},"docker compose ps\n",[53,10164,10165],{"__ignoreMap":174},[178,10166,10167,10169,10171],{"class":180,"line":181},[178,10168,4277],{"class":198},[178,10170,4280],{"class":610},[178,10172,10173],{"class":610}," ps\n",[14,10175,10176],{},"You should see three healthy containers:",[145,10178,10179,10186,10197],{},[148,10180,10181,10183,10184],{},[53,10182,4981],{}," on port ",[53,10185,4865],{},[148,10187,10188,10190,10191,10193,10194,10196],{},[53,10189,4895],{}," on ports ",[53,10192,4910],{}," (S3 API) and ",[53,10195,4920],{}," (Console)",[148,10198,10199,10183,10201],{},[53,10200,4953],{},[53,10202,4966],{},[18,10204,10206],{"id":10205},"step-4-install-dependencies","Step 4 — Install Dependencies",[169,10208,10210],{"className":1825,"code":10209,"language":1827,"meta":174,"style":174},"npm install\n",[53,10211,10212],{"__ignoreMap":174},[178,10213,10214,10216],{"class":180,"line":181},[178,10215,1839],{"class":198},[178,10217,4302],{"class":610},[18,10219,10221],{"id":10220},"step-5-start-the-dev-server","Step 5 — Start the Dev Server",[169,10223,10225],{"className":1825,"code":10224,"language":1827,"meta":174,"style":174},"npm run dev\n",[53,10226,10227],{"__ignoreMap":174},[178,10228,10229,10231,10233],{"class":180,"line":181},[178,10230,1839],{"class":198},[178,10232,1842],{"class":610},[178,10234,4320],{"class":610},[14,10236,10237,10238,10241],{},"Open ",[137,10239,4326],{"href":4326,"rel":10240},[141]," in your browser. You should see the Reqcore landing page.",[18,10243,10245],{"id":10244},"local-services","Local Services",[14,10247,10248],{},"Once running, you have access to:",[23,10250,10251,10263],{},[26,10252,10253],{},[29,10254,10255,10258,10261],{},[32,10256,10257],{},"Service",[32,10259,10260],{},"URL",[32,10262,2254],{},[39,10264,10265,10278,10291,10305,10318],{},[29,10266,10267,10270,10275],{},[44,10268,10269],{},"Reqcore App",[44,10271,10272],{},[137,10273,4326],{"href":4326,"rel":10274},[141],[44,10276,10277],{},"The application",[29,10279,10280,10282,10288],{},[44,10281,4954],{},[44,10283,10284],{},[137,10285,10286],{"href":10286,"rel":10287},"http:\u002F\u002Flocalhost:8080",[141],[44,10289,10290],{},"Database GUI",[29,10292,10293,10296,10302],{},[44,10294,10295],{},"MinIO Console",[44,10297,10298],{},[137,10299,10300],{"href":10300,"rel":10301},"http:\u002F\u002Flocalhost:9001",[141],[44,10303,10304],{},"Object storage GUI",[29,10306,10307,10310,10315],{},[44,10308,10309],{},"MinIO S3 API",[44,10311,10312],{},[137,10313,9405],{"href":9405,"rel":10314},[141],[44,10316,10317],{},"S3 endpoint",[29,10319,10320,10323,10328],{},[44,10321,10322],{},"PostgreSQL",[44,10324,10325],{},[53,10326,10327],{},"localhost:5432",[44,10329,10330],{},"Direct database access",[18,10332,10334],{"id":10333},"stopping-services","Stopping Services",[169,10336,10338],{"className":1825,"code":10337,"language":1827,"meta":174,"style":174},"# Stop the Nuxt dev server\n# Press Ctrl+C in the terminal\n\n# Stop Docker containers\ndocker compose down\n\n# Stop and remove all data (fresh start)\ndocker compose down -v\n",[53,10339,10340,10345,10350,10354,10359,10368,10372,10377],{"__ignoreMap":174},[178,10341,10342],{"class":180,"line":181},[178,10343,10344],{"class":184},"# Stop the Nuxt dev server\n",[178,10346,10347],{"class":180,"line":188},[178,10348,10349],{"class":184},"# Press Ctrl+C in the terminal\n",[178,10351,10352],{"class":180,"line":225},[178,10353,404],{"emptyLinePlaceholder":403},[178,10355,10356],{"class":180,"line":259},[178,10357,10358],{"class":184},"# Stop Docker containers\n",[178,10360,10361,10363,10365],{"class":180,"line":273},[178,10362,4277],{"class":198},[178,10364,4280],{"class":610},[178,10366,10367],{"class":610}," down\n",[178,10369,10370],{"class":180,"line":279},[178,10371,404],{"emptyLinePlaceholder":403},[178,10373,10374],{"class":180,"line":658},[178,10375,10376],{"class":184},"# Stop and remove all data (fresh start)\n",[178,10378,10379,10381,10383,10385],{"class":180,"line":671},[178,10380,4277],{"class":198},[178,10382,4280],{"class":610},[178,10384,5126],{"class":610},[178,10386,5129],{"class":234},[18,10388,8362],{"id":8361},[164,10390,10392],{"id":10391},"port-conflicts","Port conflicts",[14,10394,10395,10396,10399],{},"If port 3000 is busy, Nuxt will automatically pick the next available port. For Docker services, check ",[53,10397,10398],{},"docker compose ps"," and stop conflicting containers.",[164,10401,10403],{"id":10402},"database-connection-errors","Database connection errors",[14,10405,10406,10407,10409,10410,10412],{},"Verify PostgreSQL is running and the ",[53,10408,5244],{}," in ",[53,10411,4931],{}," matches your Docker Compose config:",[169,10414,10416],{"className":1825,"code":10415,"language":1827,"meta":174,"style":174},"docker compose logs postgres\n",[53,10417,10418],{"__ignoreMap":174},[178,10419,10420,10422,10424,10426],{"class":180,"line":181},[178,10421,4277],{"class":198},[178,10423,4280],{"class":610},[178,10425,5084],{"class":610},[178,10427,5108],{"class":610},[164,10429,10431],{"id":10430},"minio-connection-errors","MinIO connection errors",[14,10433,10434],{},"Check that MinIO is healthy:",[169,10436,10438],{"className":1825,"code":10437,"language":1827,"meta":174,"style":174},"docker compose logs minio\n",[53,10439,10440],{"__ignoreMap":174},[178,10441,10442,10444,10446,10448],{"class":180,"line":181},[178,10443,4277],{"class":198},[178,10445,4280],{"class":610},[178,10447,5084],{"class":610},[178,10449,10450],{"class":610}," minio\n",[14,10452,10453,10454,10456,10457,3083,10459,9759],{},"The default credentials are in your ",[53,10455,4931],{}," file (",[53,10458,4934],{},[53,10460,4937],{},[18,10462,763],{"id":762},[145,10464,10465,10470],{},[148,10466,10467,10469],{},[137,10468,9326],{"href":9881}," — Customize environment variables and settings",[148,10471,10472,10474],{},[137,10473,4765],{"href":7374}," — Create your first job and hire your first candidate",[788,10476,10477],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":174,"searchDepth":188,"depth":188,"links":10479},[10480,10483,10484,10485,10486,10487,10488,10489,10490,10495],{"id":4171,"depth":188,"text":4172,"children":10481},[10482],{"id":9987,"depth":225,"text":9988},{"id":10049,"depth":188,"text":10050},{"id":10086,"depth":188,"text":10087},{"id":10137,"depth":188,"text":10138},{"id":10205,"depth":188,"text":10206},{"id":10220,"depth":188,"text":10221},{"id":10244,"depth":188,"text":10245},{"id":10333,"depth":188,"text":10334},{"id":8361,"depth":188,"text":8362,"children":10491},[10492,10493,10494],{"id":10391,"depth":225,"text":10392},{"id":10402,"depth":225,"text":10403},{"id":10430,"depth":225,"text":10431},{"id":762,"depth":188,"text":763},"Step-by-step guide to install Reqcore locally using Docker Compose. Covers prerequisites, cloning the repository, and starting all services.",{},"\u002Fdocs\u002Fgetting-started\u002Finstallation",{"title":9887,"description":10496},"docs\u002F1.getting-started\u002F2.installation","iYUit7JkhciNc5F6PMPj5qnNqVK2FiT7p00w7QfSRc4",{"id":10503,"title":4711,"body":10504,"description":10815,"extension":806,"icon":10816,"meta":10817,"navigation":403,"path":4710,"section":4204,"seo":10818,"stem":10819,"__hash__":10820},"docs\u002Fdocs\u002F1.getting-started\u002F1.introduction.md",{"type":7,"value":10505,"toc":10807},[10506,10509,10516,10520,10523,10551,10554,10558,10612,10616,10621,10692,10696,10750,10754,10757,10780,10788,10790],[10,10507,4711],{"id":10508},"introduction",[14,10510,10511,10512,10515],{},"Reqcore is an open-source applicant tracking system (ATS) built for teams that refuse to rent access to their own hiring data. It runs on ",[47,10513,10514],{},"your"," infrastructure, charges no per-seat fees, and publishes every line of code for inspection.",[18,10517,10519],{"id":10518},"why-reqcore-exists","Why Reqcore Exists",[14,10521,10522],{},"Modern ATS platforms suffer from three structural problems:",[712,10524,10525,10535,10545],{},[148,10526,10527,10530,10531,10534],{},[47,10528,10529],{},"Data hostage"," — Companies pay for ",[5475,10532,10533],{},"access"," to their own candidate data. If the subscription lapses, the talent pool vanishes.",[148,10536,10537,10540,10541,10544],{},[47,10538,10539],{},"Opaque AI"," — Incumbent platforms use proprietary algorithms to rank candidates. Recruiters cannot see ",[5475,10542,10543],{},"why"," a candidate was surfaced or rejected — creating legal and ethical liability.",[148,10546,10547,10550],{},[47,10548,10549],{},"Per-seat tax"," — Adding a hiring manager or recruiter increases the software bill, punishing growing teams.",[14,10552,10553],{},"Reqcore is the antidote: an open-source ATS where you own the data, the code is public, and pricing does not scale with headcount.",[18,10555,10557],{"id":10556},"core-principles","Core Principles",[23,10559,10560,10570],{},[26,10561,10562],{},[29,10563,10564,10567],{},[32,10565,10566],{},"Principle",[32,10568,10569],{},"What It Means",[39,10571,10572,10582,10592,10602],{},[29,10573,10574,10579],{},[44,10575,10576],{},[47,10577,10578],{},"Ownership over access",[44,10580,10581],{},"You own the infrastructure (Postgres + MinIO). Your talent pool is a permanent asset — not a subscription.",[29,10583,10584,10589],{},[44,10585,10586],{},[47,10587,10588],{},"Auditable intelligence",[44,10590,10591],{},"AI ranking is planned. When it ships, it will expose a visible Matching Logic summary so recruiters can verify results.",[29,10593,10594,10599],{},[44,10595,10596],{},[47,10597,10598],{},"No per-seat pricing",[44,10600,10601],{},"Scale your hiring team without increasing your software bill. Add as many users as you need.",[29,10603,10604,10609],{},[44,10605,10606],{},[47,10607,10608],{},"Runs on your network",[44,10610,10611],{},"With local storage (MinIO) and local AI (Ollama), sensitive candidate PII never has to leave your network.",[18,10613,10615],{"id":10614},"technology-stack","Technology Stack",[14,10617,2175,10618,10620],{},[47,10619,2178],{}," full-stack application:",[23,10622,10623,10633],{},[26,10624,10625],{},[29,10626,10627,10630],{},[32,10628,10629],{},"Layer",[32,10631,10632],{},"Technology",[39,10634,10635,10643,10649,10656,10664,10671,10677,10685],{},[29,10636,10637,10640],{},[44,10638,10639],{},"Framework",[44,10641,10642],{},"Nuxt 4 (Vue 3 + Nitro)",[29,10644,10645,10647],{},[44,10646,6023],{},[44,10648,826],{},[29,10650,10651,10654],{},[44,10652,10653],{},"ORM",[44,10655,830],{},[29,10657,10658,10661],{},[44,10659,10660],{},"Auth",[44,10662,10663],{},"Better Auth + Org plugin",[29,10665,10666,10668],{},[44,10667,582],{},[44,10669,10670],{},"S3-compatible (MinIO \u002F Railway Buckets)",[29,10672,10673,10675],{},[44,10674,4553],{},[44,10676,3939],{},[29,10678,10679,10682],{},[44,10680,10681],{},"SEO",[44,10683,10684],{},"@nuxtjs\u002Fseo + @nuxt\u002Fcontent v3",[29,10686,10687,10690],{},[44,10688,10689],{},"Icons",[44,10691,4565],{},[18,10693,10695],{"id":10694},"who-is-reqcore-for","Who Is Reqcore For?",[23,10697,10698,10708],{},[26,10699,10700],{},[29,10701,10702,10705],{},[32,10703,10704],{},"Persona",[32,10706,10707],{},"Primary Need",[39,10709,10710,10720,10730,10740],{},[29,10711,10712,10717],{},[44,10713,10714],{},[47,10715,10716],{},"Recruiter",[44,10718,10719],{},"Fast candidate pipeline, clear application workflow",[29,10721,10722,10727],{},[44,10723,10724],{},[47,10725,10726],{},"Hiring Manager",[44,10728,10729],{},"Clear candidate comparisons, proof-based recommendations",[29,10731,10732,10737],{},[44,10733,10734],{},[47,10735,10736],{},"HR Administrator",[44,10738,10739],{},"Multi-tenant control, data ownership, audit trails",[29,10741,10742,10747],{},[44,10743,10744],{},[47,10745,10746],{},"Engineering\u002FIT",[44,10748,10749],{},"Simple self-hosting via Docker Compose, clear documentation",[18,10751,10753],{"id":10752},"current-status","Current Status",[14,10755,10756],{},"Reqcore is under active development. The MVP is complete and includes:",[145,10758,10759,10762,10765,10768,10771,10774,10777],{},[148,10760,10761],{},"Multi-tenant organizations with role-based membership",[148,10763,10764],{},"Job management with status workflow (draft → open → closed → archived)",[148,10766,10767],{},"Candidate pipeline with drag-and-drop Kanban board",[148,10769,10770],{},"Document storage for resumes and cover letters via S3-compatible storage",[148,10772,10773],{},"Public job board with SEO-friendly URLs",[148,10775,10776],{},"Custom application forms per job",[148,10778,10779],{},"Recruiter dashboard with at-a-glance stats",[14,10781,10782,10783,10787],{},"See the ",[137,10784,10786],{"href":10785},"\u002Froadmap","Roadmap"," for what's shipping next.",[18,10789,763],{"id":762},[145,10791,10792,10797,10802],{},[148,10793,10794,10796],{},[137,10795,9887],{"href":10498}," — Get Reqcore running locally in under 5 minutes",[148,10798,10799,10801],{},[137,10800,4765],{"href":7374}," — Create your first job and post it publicly",[148,10803,10804,10806],{},[137,10805,771],{"href":770}," — Understand how the system is designed",{"title":174,"searchDepth":188,"depth":188,"links":10808},[10809,10810,10811,10812,10813,10814],{"id":10518,"depth":188,"text":10519},{"id":10556,"depth":188,"text":10557},{"id":10614,"depth":188,"text":10615},{"id":10694,"depth":188,"text":10695},{"id":10752,"depth":188,"text":10753},{"id":762,"depth":188,"text":763},"What is Reqcore and why it exists. An overview of the open-source applicant tracking system built for teams that want full data ownership.","book-open",{},{"title":4711,"description":10815},"docs\u002F1.getting-started\u002F1.introduction","9BmVwYzZRPLibAjuxI8TtXUDSYUJdpGER9lV6CrDt2M",{"id":10822,"title":4765,"body":10823,"description":11078,"extension":806,"icon":11079,"meta":11080,"navigation":403,"path":7374,"section":4204,"seo":11081,"stem":11082,"__hash__":11083},"docs\u002Fdocs\u002F1.getting-started\u002F4.quick-start.md",{"type":7,"value":10824,"toc":11068},[10825,10827,10833,10837,10858,10862,10865,10878,10881,10885,10925,10931,10935,10966,10970,10985,10994,10998,11001,11020,11024,11029,11043,11047],[10,10826,4765],{"id":4764},[14,10828,10829,10830,835],{},"This guide walks you through the complete flow: sign up, create an organization, post a job, and see an application come in. Assumes you have Reqcore ",[137,10831,10832],{"href":10498},"installed and running",[18,10834,10836],{"id":10835},"_1-create-an-account","1. Create an Account",[712,10838,10839,10844,10850,10853],{},[148,10840,10237,10841],{},[137,10842,4326],{"href":4326,"rel":10843},[141],[148,10845,5996,10846,10849],{},[47,10847,10848],{},"Get Started"," in the top-right corner",[148,10851,10852],{},"Enter your name, email, and password",[148,10854,5996,10855],{},[47,10856,10857],{},"Sign Up",[18,10859,10861],{"id":10860},"_2-create-an-organization","2. Create an Organization",[14,10863,10864],{},"After signing up, you'll be redirected to create your first organization:",[712,10866,10867,10870,10873],{},[148,10868,10869],{},"Enter your company name (e.g., \"Acme Corp\")",[148,10871,10872],{},"A URL-friendly slug is generated automatically",[148,10874,5996,10875],{},[47,10876,10877],{},"Create Organization",[14,10879,10880],{},"Your organization is now set up. All data — jobs, candidates, applications — is scoped to this organization. Team members you invite later will share the same data.",[18,10882,10884],{"id":10883},"_3-create-a-job","3. Create a Job",[712,10886,10887,10896,10921],{},[148,10888,10889,10890,10892,10893,10895],{},"From the dashboard, click ",[47,10891,7311],{}," (or navigate to ",[47,10894,8586],{}," in the sidebar)",[148,10897,10898,10899],{},"Fill in the job details:\n",[145,10900,10901,10906,10911,10916],{},[148,10902,10903,10905],{},[47,10904,8610],{},": e.g., \"Senior Software Engineer\"",[148,10907,10908,10910],{},[47,10909,872],{},": Job responsibilities and requirements (Markdown supported)",[148,10912,10913,10915],{},[47,10914,8645],{},": Office location or \"Remote\"",[148,10917,10918,10920],{},[47,10919,8655],{},": Full-time, Part-time, Contract, etc.",[148,10922,5996,10923],{},[47,10924,7311],{},[14,10926,10927,10928,10930],{},"The job starts in ",[47,10929,8533],{}," status — it's not visible publicly yet.",[18,10932,10934],{"id":10933},"_4-add-custom-application-questions","4. Add Custom Application Questions",[712,10936,10937,10942,10961,10963],{},[148,10938,10939,10940,6497],{},"From the job detail page, click the ",[47,10941,6496],{},[148,10943,5996,10944,10946,10947],{},[47,10945,6502],{}," to create custom fields:\n",[145,10948,10949,10952,10955,10958],{},[148,10950,10951],{},"Text fields (short answer, long answer)",[148,10953,10954],{},"Select dropdowns (single or multiple choice)",[148,10956,10957],{},"File upload (for portfolios, work samples)",[148,10959,10960],{},"URL, email, phone, number, date fields",[148,10962,6533],{},[148,10964,10965],{},"Each question can be marked as required or optional",[18,10967,10969],{"id":10968},"_5-open-the-job-for-applications","5. Open the Job for Applications",[712,10971,10972,10975,10982],{},[148,10973,10974],{},"On the job detail page, click the status badge dropdown",[148,10976,10977,10978,1975,10980],{},"Change status from ",[47,10979,8533],{},[47,10981,6557],{},[148,10983,10984],{},"A shareable application link appears — copy it or share directly",[14,10986,10987,10988,10991,10992,835],{},"The job is now live on your ",[137,10989,10990],{"href":6733},"public job board"," at ",[53,10993,8933],{},[18,10995,10997],{"id":10996},"_6-view-incoming-applications","6. View Incoming Applications",[14,10999,11000],{},"When candidates apply through the public form:",[712,11002,11003,11008,11014,11017],{},[148,11004,6493,11005,11007],{},[47,11006,6876],{}," tab to see the Kanban board",[148,11009,11010,11011,11013],{},"New applications appear in the ",[47,11012,6020],{}," column",[148,11015,11016],{},"Drag candidates through stages: Screening → Interview → Offer → Hired",[148,11018,11019],{},"Click any candidate card to see their details, resume, and question responses in the sidebar",[18,11021,11023],{"id":11022},"_7-manage-candidates","7. Manage Candidates",[14,11025,1954,11026,11028],{},[47,11027,6958],{}," section in the sidebar shows your full candidate pool:",[145,11030,11031,11034,11037,11040],{},[148,11032,11033],{},"Candidates are automatically created when someone applies",[148,11035,11036],{},"Each candidate can have multiple applications across different jobs",[148,11038,11039],{},"Upload additional documents (resumes, cover letters) from the candidate detail page",[148,11041,11042],{},"View inline PDF previews directly in the browser",[18,11044,11046],{"id":11045},"whats-next","What's Next?",[145,11048,11049,11054,11059,11064],{},[148,11050,11051,11053],{},[137,11052,7363],{"href":7362}," — Learn about job status workflows and SEO fields",[148,11055,11056,11058],{},[137,11057,6741],{"href":6740}," — Master the Kanban board and status transitions",[148,11060,11061,11063],{},[137,11062,6748],{"href":6747}," — Upload, preview, and manage candidate files",[148,11065,11066,6316],{},[137,11067,771],{"href":770},{"title":174,"searchDepth":188,"depth":188,"links":11069},[11070,11071,11072,11073,11074,11075,11076,11077],{"id":10835,"depth":188,"text":10836},{"id":10860,"depth":188,"text":10861},{"id":10883,"depth":188,"text":10884},{"id":10933,"depth":188,"text":10934},{"id":10968,"depth":188,"text":10969},{"id":10996,"depth":188,"text":10997},{"id":11022,"depth":188,"text":11023},{"id":11045,"depth":188,"text":11046},"Create your first organization, post a job, and receive applications in under 10 minutes with Reqcore.","rocket",{},{"title":4765,"description":11078},"docs\u002F1.getting-started\u002F4.quick-start","hN02iUXImYVDuV8f37mFtCPSSdgAo9VP_oRA9ZSX6g4",{"id":11085,"title":11086,"body":11087,"description":11974,"extension":806,"icon":11975,"meta":11976,"navigation":403,"path":11977,"section":11978,"seo":11979,"stem":11980,"__hash__":11981},"docs\u002Fdocs\u002F6.legal\u002F1.privacy-policy.md","Privacy Policy",{"type":7,"value":11088,"toc":11952},[11089,11092,11102,11126,11135,11137,11141,11144,11158,11164,11166,11170,11174,11177,11212,11216,11223,11237,11243,11247,11258,11263,11307,11315,11320,11346,11349,11387,11391,11394,11402,11405,11407,11411,11414,11502,11507,11509,11513,11516,11547,11553,11567,11569,11573,11576,11620,11622,11626,11629,11693,11696,11703,11705,11709,11763,11765,11769,11772,11776,11819,11827,11830,11834,11837,11851,11857,11859,11863,11866,11889,11895,11897,11901,11909,11911,11915,11918,11921,11923,11927,11930,11949],[10,11090,11086],{"id":11091},"privacy-policy",[14,11093,11094,11097,11098,11101],{},[47,11095,11096],{},"Effective date:"," March 9, 2026\n",[47,11099,11100],{},"Last updated:"," March 9, 2026",[14,11103,11104,11105,11107,11108,11107,11111,11114,11115,11118,11119,11122,11123,11125],{},"Reqcore, Inc. (\"",[47,11106,7940],{},",\" \"",[47,11109,11110],{},"we",[47,11112,11113],{},"us",",\" or \"",[47,11116,11117],{},"our","\") operates the Reqcore applicant tracking system available at ",[137,11120,5437],{"href":5437,"rel":11121},[141]," (the \"",[47,11124,10257],{},"\"). This Privacy Policy explains what personal data we collect, why we collect it, how we use it, and what rights you have.",[14,11127,11128,11129,835],{},"If you have questions about this policy, contact us at ",[47,11130,11131],{},[137,11132,11134],{"href":11133},"mailto:privacy@reqcore.com","privacy@reqcore.com",[7900,11136],{},[18,11138,11140],{"id":11139},"_1-scope","1. Scope",[14,11142,11143],{},"This policy applies to:",[145,11145,11146,11149,11152,11155],{},[148,11147,11148],{},"Visitors to reqcore.com and its subdomains",[148,11150,11151],{},"Candidates who apply to jobs through the public job board",[148,11153,11154],{},"Registered users (recruiters, hiring managers, administrators) who operate the Service",[148,11156,11157],{},"Self-hosted instances only insofar as they connect to Reqcore-operated services (e.g., optional analytics)",[14,11159,11160,11161,11163],{},"Self-hosted deployments operate on ",[47,11162,10514],{}," infrastructure. Reqcore has no access to data stored in self-hosted instances unless you explicitly configure a connection to our services.",[7900,11165],{},[18,11167,11169],{"id":11168},"_2-data-we-collect","2. Data We Collect",[164,11171,11173],{"id":11172},"_21-account-data","2.1 Account Data",[14,11175,11176],{},"When you create an account, we collect:",[23,11178,11179,11188],{},[26,11180,11181],{},[29,11182,11183,11186],{},[32,11184,11185],{},"Data",[32,11187,2254],{},[39,11189,11190,11198,11205],{},[29,11191,11192,11195],{},[44,11193,11194],{},"Name",[44,11196,11197],{},"Display in the application, team collaboration",[29,11199,11200,11202],{},[44,11201,6465],{},[44,11203,11204],{},"Authentication, account recovery, notifications",[29,11206,11207,11209],{},[44,11208,8964],{},[44,11210,11211],{},"Multi-tenant isolation",[164,11213,11215],{"id":11214},"_22-candidate-application-data","2.2 Candidate Application Data",[14,11217,11218,11219,11222],{},"When a candidate submits an application through the public job board, the ",[47,11220,11221],{},"organization operating the job board"," is the data controller for that application data. Reqcore acts as a data processor. Application data may include:",[145,11224,11225,11228,11231,11234],{},[148,11226,11227],{},"Name, email, phone number",[148,11229,11230],{},"Resume \u002F CV and cover letter",[148,11232,11233],{},"Responses to custom application questions",[148,11235,11236],{},"Any files uploaded during the application process",[14,11238,11239,11240,11242],{},"This data is stored in the organization's database and object storage (MinIO\u002FS3) and is ",[47,11241,2092],{}," shared with Reqcore or any third party. For self-hosted deployments, all data remains on the operator's infrastructure.",[164,11244,11246],{"id":11245},"_23-analytics-data","2.3 Analytics Data",[14,11248,11249,11250,11253,11254,11257],{},"We use ",[47,11251,11252],{},"PostHog"," (EU instance: eu.i.posthog.com) for product analytics on the hosted version at reqcore.com. Analytics data is collected ",[47,11255,11256],{},"only after you grant explicit consent"," via the cookie banner.",[14,11259,11260],{},[47,11261,11262],{},"What we collect when you opt in:",[23,11264,11265,11273],{},[26,11266,11267],{},[29,11268,11269,11271],{},[32,11270,11185],{},[32,11272,2254],{},[39,11274,11275,11283,11291,11299],{},[29,11276,11277,11280],{},[44,11278,11279],{},"Page views and page leave events",[44,11281,11282],{},"Understand which features are used",[29,11284,11285,11288],{},[44,11286,11287],{},"Anonymized user ID (UUID, not your name)",[44,11289,11290],{},"Distinguish unique sessions without collecting personal data",[29,11292,11293,11296],{},[44,11294,11295],{},"Organization ID and name (for logged-in users)",[44,11297,11298],{},"Aggregate feature usage by organization",[29,11300,11301,11304],{},[44,11302,11303],{},"Browser and device metadata",[44,11305,11306],{},"Ensure compatibility across platforms",[4946,11308,11309],{},[14,11310,11311,11314],{},[47,11312,11313],{},"Data minimisation",": Only user IDs (opaque UUIDs) are sent — not names, email addresses, or account creation dates. URL query parameters and fragments are stripped from all captured URLs before transmission to prevent accidental token or PII leakage.",[14,11316,11317],{},[47,11318,11319],{},"What we do NOT collect:",[145,11321,11322,11325,11328,11331,11334,11337,11340,11343],{},[148,11323,11324],{},"Your name, email address, or any directly identifying personal data",[148,11326,11327],{},"Session recordings",[148,11329,11330],{},"Autocapture \u002F DOM interaction tracking",[148,11332,11333],{},"Console logs",[148,11335,11336],{},"Form inputs or keystrokes",[148,11338,11339],{},"Survey responses",[148,11341,11342],{},"Candidate application content",[148,11344,11345],{},"URL query parameters or fragments (stripped before capture)",[14,11347,11348],{},"PostHog is configured with the following privacy settings:",[145,11350,11351,11357,11363,11369,11375,11381],{},[148,11352,11353,11356],{},[53,11354,11355],{},"opt_out_capturing_by_default: true"," — No data is collected until you consent",[148,11358,11359,11362],{},[53,11360,11361],{},"respect_dnt: true"," — We honor Do Not Track browser signals",[148,11364,11365,11368],{},[53,11366,11367],{},"autocapture: false"," — No automatic click\u002Fform\u002Finput tracking",[148,11370,11371,11374],{},[53,11372,11373],{},"disable_session_recording: true"," — No screen recordings",[148,11376,11377,11380],{},[53,11378,11379],{},"secure_cookie: true"," — Cookies are only transmitted over HTTPS",[148,11382,11383,11386],{},[53,11384,11385],{},"cross_subdomain_cookie: false"," — No cross-subdomain tracking",[164,11388,11390],{"id":11389},"_24-technical-data","2.4 Technical Data",[14,11392,11393],{},"Our servers automatically log:",[145,11395,11396,11399],{},[148,11397,11398],{},"IP addresses (for rate limiting and abuse prevention, not stored long-term)",[148,11400,11401],{},"HTTP request metadata (method, path, status code, user agent)",[14,11403,11404],{},"These logs are used for security monitoring and are rotated regularly.",[7900,11406],{},[18,11408,11410],{"id":11409},"_3-cookies-and-local-storage","3. Cookies and Local Storage",[14,11412,11413],{},"Reqcore uses a minimal set of cookies and local storage entries:",[23,11415,11416,11429],{},[26,11417,11418],{},[29,11419,11420,11422,11424,11426],{},[32,11421,11194],{},[32,11423,869],{},[32,11425,2254],{},[32,11427,11428],{},"Duration",[39,11430,11431,11447,11470,11485],{},[29,11432,11433,11438,11441,11444],{},[44,11434,11435],{},[53,11436,11437],{},"better-auth.session_token",[44,11439,11440],{},"HTTP-only cookie",[44,11442,11443],{},"Session authentication",[44,11445,11446],{},"Session (expires on logout or after configured timeout)",[29,11448,11449,11454,11457,11467],{},[44,11450,11451],{},[53,11452,11453],{},"reqcore-consent",[44,11455,11456],{},"Cookie",[44,11458,11459,11460,4623,11463,11466],{},"Stores your analytics consent choice (",[53,11461,11462],{},"granted",[53,11464,11465],{},"denied","). Shared across reqcore.com and app.reqcore.com via a cross-subdomain cookie.",[44,11468,11469],{},"1 year",[29,11471,11472,11477,11479,11482],{},[44,11473,11474],{},[53,11475,11476],{},"reqcore_i18n_redirected",[44,11478,11456],{},[44,11480,11481],{},"Prevents repeated language-detection redirects",[44,11483,11484],{},"Session",[29,11486,11487,11493,11496,11499],{},[44,11488,11489,11490,4575],{},"PostHog cookies (",[53,11491,11492],{},"ph_*",[44,11494,11495],{},"Cookie + Local storage",[44,11497,11498],{},"Analytics session identification (only set after consent)",[44,11500,11501],{},"Up to 1 year",[14,11503,11504],{},[47,11505,11506],{},"No third-party advertising cookies are used. No data is sold to third parties.",[7900,11508],{},[18,11510,11512],{"id":11511},"_4-how-we-use-your-data","4. How We Use Your Data",[14,11514,11515],{},"We use personal data for the following purposes:",[712,11517,11518,11524,11530,11535,11541],{},[148,11519,11520,11523],{},[47,11521,11522],{},"Provide the Service"," — Authenticate users, manage organizations, process job applications",[148,11525,11526,11529],{},[47,11527,11528],{},"Improve the Service"," — Analyze aggregated usage patterns to prioritize features (analytics, opt-in only)",[148,11531,11532,11534],{},[47,11533,5],{}," — Rate limiting, abuse prevention, audit logging",[148,11536,11537,11540],{},[47,11538,11539],{},"Communication"," — Account-related emails (password resets, critical security notices)",[148,11542,11543,11546],{},[47,11544,11545],{},"Legal compliance"," — Respond to lawful requests from authorities",[14,11548,11549,11550,11552],{},"We do ",[47,11551,2092],{}," use personal data for:",[145,11554,11555,11558,11561,11564],{},[148,11556,11557],{},"Advertising or ad targeting",[148,11559,11560],{},"Selling or renting to third parties",[148,11562,11563],{},"Automated decision-making or profiling that produces legal effects",[148,11565,11566],{},"Training AI \u002F machine learning models on your data",[7900,11568],{},[18,11570,11572],{"id":11571},"_5-legal-bases-for-processing-gdpr","5. Legal Bases for Processing (GDPR)",[14,11574,11575],{},"If you are in the European Economic Area (EEA), United Kingdom, or Switzerland, we process personal data under these legal bases:",[23,11577,11578,11587],{},[26,11579,11580],{},[29,11581,11582,11584],{},[32,11583,2254],{},[32,11585,11586],{},"Legal basis",[39,11588,11589,11597,11605,11613],{},[29,11590,11591,11594],{},[44,11592,11593],{},"Account management and authentication",[44,11595,11596],{},"Performance of contract (Art. 6(1)(b) GDPR)",[29,11598,11599,11602],{},[44,11600,11601],{},"Analytics",[44,11603,11604],{},"Consent (Art. 6(1)(a) GDPR) — opt-in via cookie banner",[29,11606,11607,11610],{},[44,11608,11609],{},"Security and abuse prevention",[44,11611,11612],{},"Legitimate interest (Art. 6(1)(f) GDPR)",[29,11614,11615,11617],{},[44,11616,11545],{},[44,11618,11619],{},"Legal obligation (Art. 6(1)(c) GDPR)",[7900,11621],{},[18,11623,11625],{"id":11624},"_6-data-sharing-and-sub-processors","6. Data Sharing and Sub-processors",[14,11627,11628],{},"We share personal data only with the following categories of service providers:",[23,11630,11631,11642],{},[26,11632,11633],{},[29,11634,11635,11638,11640],{},[32,11636,11637],{},"Sub-processor",[32,11639,2254],{},[32,11641,8645],{},[39,11643,11644,11656,11668,11681],{},[29,11645,11646,11650,11653],{},[44,11647,11648],{},[47,11649,11252],{},[44,11651,11652],{},"Product analytics (opt-in only)",[44,11654,11655],{},"EU (eu.i.posthog.com)",[29,11657,11658,11662,11665],{},[44,11659,11660],{},[47,11661,5955],{},[44,11663,11664],{},"Application hosting",[44,11666,11667],{},"US",[29,11669,11670,11675,11678],{},[44,11671,11672],{},[47,11673,11674],{},"Cloudflare",[44,11676,11677],{},"CDN and DDoS protection",[44,11679,11680],{},"Global (edge network)",[29,11682,11683,11688,11691],{},[44,11684,11685],{},[47,11686,11687],{},"GitHub",[44,11689,11690],{},"Source code hosting, authentication (if configured)",[44,11692,11667],{},[14,11694,11695],{},"We do not sell, rent, or trade personal data. Data is shared with sub-processors only as necessary to operate the Service, under data processing agreements that include appropriate safeguards.",[14,11697,11698,11699,11702],{},"For ",[47,11700,11701],{},"international transfers"," from the EEA, we rely on Standard Contractual Clauses (SCCs) or adequacy decisions where applicable.",[7900,11704],{},[18,11706,11708],{"id":11707},"_7-data-retention","7. Data Retention",[23,11710,11711,11721],{},[26,11712,11713],{},[29,11714,11715,11718],{},[32,11716,11717],{},"Data type",[32,11719,11720],{},"Retention period",[39,11722,11723,11731,11739,11747,11755],{},[29,11724,11725,11728],{},[44,11726,11727],{},"Account data",[44,11729,11730],{},"Until you delete your account",[29,11732,11733,11736],{},[44,11734,11735],{},"Candidate application data",[44,11737,11738],{},"Controlled by the organization operating the job board; Reqcore does not set retention periods for processor data",[29,11740,11741,11744],{},[44,11742,11743],{},"Analytics data",[44,11745,11746],{},"Up to 24 months from collection, then automatically deleted",[29,11748,11749,11752],{},[44,11750,11751],{},"Server logs",[44,11753,11754],{},"Rotated and deleted within 90 days",[29,11756,11757,11760],{},[44,11758,11759],{},"Consent records",[44,11761,11762],{},"Retained as long as the consent is valid, plus 3 years for compliance records",[7900,11764],{},[18,11766,11768],{"id":11767},"_8-your-rights","8. Your Rights",[14,11770,11771],{},"Depending on your jurisdiction, you may have the following rights:",[164,11773,11775],{"id":11774},"_81-gdpr-rights-eea-uk-switzerland","8.1 GDPR Rights (EEA, UK, Switzerland)",[145,11777,11778,11783,11789,11795,11801,11807,11813],{},[148,11779,11780,11782],{},[47,11781,7440],{}," — Request a copy of your personal data",[148,11784,11785,11788],{},[47,11786,11787],{},"Rectification"," — Correct inaccurate data",[148,11790,11791,11794],{},[47,11792,11793],{},"Erasure"," — Request deletion of your data (\"right to be forgotten\")",[148,11796,11797,11800],{},[47,11798,11799],{},"Restriction"," — Limit how we process your data",[148,11802,11803,11806],{},[47,11804,11805],{},"Portability"," — Receive your data in a structured, machine-readable format",[148,11808,11809,11812],{},[47,11810,11811],{},"Objection"," — Object to processing based on legitimate interest",[148,11814,11815,11818],{},[47,11816,11817],{},"Withdraw consent"," — Revoke analytics consent at any time via the cookie banner or by clearing local storage",[14,11820,11821,11822,11826],{},"To exercise these rights, contact ",[47,11823,11824],{},[137,11825,11134],{"href":11133},". We will respond within 30 days.",[14,11828,11829],{},"You also have the right to lodge a complaint with your local data protection authority.",[164,11831,11833],{"id":11832},"_82-ccpa-rights-california","8.2 CCPA Rights (California)",[14,11835,11836],{},"If you are a California resident, you have the right to:",[145,11838,11839,11842,11845,11848],{},[148,11840,11841],{},"Know what personal information we collect and how it is used",[148,11843,11844],{},"Request deletion of your personal information",[148,11846,11847],{},"Opt out of the sale of personal information (we do not sell personal information)",[148,11849,11850],{},"Non-discrimination for exercising your rights",[14,11852,11821,11853,835],{},[47,11854,11855],{},[137,11856,11134],{"href":11133},[7900,11858],{},[18,11860,11862],{"id":11861},"_9-security","9. Security",[14,11864,11865],{},"We implement appropriate technical and organizational measures to protect personal data, including:",[145,11867,11868,11871,11874,11877,11880,11883,11886],{},[148,11869,11870],{},"HTTPS-only connections with HSTS",[148,11872,11873],{},"HTTP-only, secure session cookies",[148,11875,11876],{},"Server-side session storage",[148,11878,11879],{},"Tenant-isolated database queries",[148,11881,11882],{},"Rate limiting on public endpoints",[148,11884,11885],{},"Input validation with Zod schemas on all API endpoints",[148,11887,11888],{},"Proxied document access (no direct S3\u002FMinIO URLs exposed)",[14,11890,11891,11892,835],{},"For more details, see our ",[137,11893,11894],{"href":809},"Security documentation",[7900,11896],{},[18,11898,11900],{"id":11899},"_10-childrens-privacy","10. Children's Privacy",[14,11902,11903,11904,11908],{},"The Service is not directed at children under 16. We do not knowingly collect personal data from children. If you believe a child has provided us with personal data, contact ",[47,11905,11906],{},[137,11907,11134],{"href":11133}," and we will delete it promptly.",[7900,11910],{},[18,11912,11914],{"id":11913},"_11-changes-to-this-policy","11. Changes to This Policy",[14,11916,11917],{},"We may update this Privacy Policy from time to time. Material changes will be communicated through the Service or via email. The \"Last updated\" date at the top of this page indicates when the policy was most recently revised.",[14,11919,11920],{},"Continued use of the Service after changes take effect constitutes acceptance of the updated policy.",[7900,11922],{},[18,11924,11926],{"id":11925},"_12-contact","12. Contact",[14,11928,11929],{},"For privacy-related inquiries:",[145,11931,11932,11939],{},[148,11933,11934,10077,11937],{},[47,11935,11936],{},"Email:",[137,11938,11134],{"href":11133},[148,11940,11941,10077,11944],{},[47,11942,11943],{},"GitHub:",[137,11945,11948],{"href":11946,"rel":11947},"https:\u002F\u002Fgithub.com\u002Freqcore-inc\u002Freqcore",[141],"github.com\u002Freqcore-inc\u002Freqcore",[14,11950,11951],{},"If you are in the EEA and believe we have not adequately addressed your concerns, you may contact your local data protection authority.",{"title":174,"searchDepth":188,"depth":188,"links":11953},[11954,11955,11961,11962,11963,11964,11965,11966,11970,11971,11972,11973],{"id":11139,"depth":188,"text":11140},{"id":11168,"depth":188,"text":11169,"children":11956},[11957,11958,11959,11960],{"id":11172,"depth":225,"text":11173},{"id":11214,"depth":225,"text":11215},{"id":11245,"depth":225,"text":11246},{"id":11389,"depth":225,"text":11390},{"id":11409,"depth":188,"text":11410},{"id":11511,"depth":188,"text":11512},{"id":11571,"depth":188,"text":11572},{"id":11624,"depth":188,"text":11625},{"id":11707,"depth":188,"text":11708},{"id":11767,"depth":188,"text":11768,"children":11967},[11968,11969],{"id":11774,"depth":225,"text":11775},{"id":11832,"depth":225,"text":11833},{"id":11861,"depth":188,"text":11862},{"id":11899,"depth":188,"text":11900},{"id":11913,"depth":188,"text":11914},{"id":11925,"depth":188,"text":11926},"How Reqcore collects, uses, and protects your personal data. Covers analytics, cookies, data retention, and your rights under GDPR and CCPA.","shield",{},"\u002Fdocs\u002Flegal\u002Fprivacy-policy","Legal",{"title":11086,"description":11974},"docs\u002F6.legal\u002F1.privacy-policy","0RfHst_zh8y_L4V2PZy9shwvPhERfOpNHF-dP1k7hAI",1777750244687]