[{"data":1,"prerenderedAt":1171},["ShallowReactive",2],{"navigation":3,"\u002Fgetting-started\u002Fbackground-maintenance":189,"\u002Fgetting-started\u002Fbackground-maintenance-surround":1166},[4,34,60,116,137,153],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":33},"Getting Started","i-lucide-rocket","\u002Fgetting-started","1.getting-started",[10,15,18,23,28],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fgetting-started\u002Fintroduction","1.getting-started\u002F1.introduction","i-lucide-house",{"title":5,"path":16,"stem":17,"icon":6},"\u002Fgetting-started\u002Fgetting-started","1.getting-started\u002F2.getting-started",{"title":19,"path":20,"stem":21,"icon":22},"Choosing a Preset","\u002Fgetting-started\u002Fchoosing-a-preset","1.getting-started\u002F3.choosing-a-preset","i-lucide-git-branch",{"title":24,"path":25,"stem":26,"icon":27},"Deployment","\u002Fgetting-started\u002Fdeployment","1.getting-started\u002F4.deployment","i-lucide-cloud",{"title":29,"path":30,"stem":31,"icon":32},"Background Maintenance","\u002Fgetting-started\u002Fbackground-maintenance","1.getting-started\u002F5.background-maintenance","i-lucide-timer-reset",false,{"title":35,"icon":36,"path":37,"stem":38,"children":39,"page":33},"Build","i-lucide-wrench","\u002Fbuild","2.build",[40,45,50,55],{"title":41,"path":42,"stem":43,"icon":44},"Routers & Prefixes","\u002Fbuild\u002Frouters-and-prefixes","2.build\u002F1.routers-and-prefixes","i-lucide-route",{"title":46,"path":47,"stem":48,"icon":49},"Configuration","\u002Fbuild\u002Fconfiguration","2.build\u002F2.configuration","i-lucide-settings",{"title":51,"path":52,"stem":53,"icon":54},"Authorization Dependencies","\u002Fbuild\u002Fauthorization-dependencies","2.build\u002F3.authorization-dependencies","i-lucide-shield-check",{"title":56,"path":57,"stem":58,"icon":59},"Command Line","\u002Fbuild\u002Fcli","2.build\u002F4.cli","i-lucide-terminal",{"title":61,"icon":62,"path":63,"stem":64,"children":65,"page":33},"Auth","i-lucide-lock","\u002Fauth","3.auth",[66,71,76,81,86,91,96,101,106,111],{"title":67,"path":68,"stem":69,"icon":70},"OAuth & Social Login","\u002Fauth\u002Foauth-and-social-login","3.auth\u002F1.oauth-and-social-login","i-lucide-log-in",{"title":72,"path":73,"stem":74,"icon":75},"Multi-Frontend Support","\u002Fauth\u002Fmulti-frontend","3.auth\u002F10.multi-frontend","i-lucide-layout-grid",{"title":77,"path":78,"stem":79,"icon":80},"Sessions & Audit","\u002Fauth\u002Fsessions-and-audit","3.auth\u002F2.sessions-and-audit","i-lucide-monitor-smartphone",{"title":82,"path":83,"stem":84,"icon":85},"Passwordless & Messaging","\u002Fauth\u002Fpasswordless-and-messaging","3.auth\u002F3.passwordless-and-messaging","i-lucide-mail",{"title":87,"path":88,"stem":89,"icon":90},"JWT Tokens","\u002Fauth\u002Fjwt-tokens","3.auth\u002F4.jwt-tokens","i-lucide-key-round",{"title":92,"path":93,"stem":94,"icon":95},"User Management API","\u002Fauth\u002Fuser-management-api","3.auth\u002F5.user-management-api","i-lucide-users",{"title":97,"path":98,"stem":99,"icon":100},"User Invitations","\u002Fauth\u002Fuser-invitations","3.auth\u002F6.user-invitations","i-lucide-send",{"title":102,"path":103,"stem":104,"icon":105},"Roles & Permissions","\u002Fauth\u002Froles-and-permissions","3.auth\u002F7.roles-and-permissions","i-lucide-shield",{"title":107,"path":108,"stem":109,"icon":110},"ABAC","\u002Fauth\u002Fabac","3.auth\u002F8.abac","i-lucide-filter",{"title":112,"path":113,"stem":114,"icon":115},"User Status","\u002Fauth\u002Fuser-status","3.auth\u002F9.user-status","i-lucide-user-cog",{"title":117,"icon":118,"path":119,"stem":120,"children":121,"page":33},"Enterprise","i-lucide-building-2","\u002Fenterprise","4.enterprise",[122,127,132],{"title":123,"path":124,"stem":125,"icon":126},"Core Authorization Concepts","\u002Fenterprise\u002Fcore-authorization-concepts","4.enterprise\u002F1.core-authorization-concepts","i-lucide-network",{"title":128,"path":129,"stem":130,"icon":131},"Entities","\u002Fenterprise\u002Fentities","4.enterprise\u002F2.entities","i-lucide-folder-tree",{"title":133,"path":134,"stem":135,"icon":136},"Entity Memberships","\u002Fenterprise\u002Fentity-memberships","4.enterprise\u002F3.entity-memberships","i-lucide-user-plus",{"title":138,"icon":139,"path":140,"stem":141,"children":142,"page":33},"Integrations","i-lucide-plug","\u002Fintegrations","5.integrations",[143,148],{"title":144,"path":145,"stem":146,"icon":147},"API Keys","\u002Fintegrations\u002Fapi-keys","5.integrations\u002F1.api-keys","i-lucide-key",{"title":149,"path":150,"stem":151,"icon":152},"OutlabsAuth UI","\u002Fintegrations\u002Foutlabsauth-ui","5.integrations\u002F2.outlabsauth-ui","i-lucide-layout-dashboard",{"title":154,"icon":155,"path":156,"stem":157,"children":158,"page":33},"Reference","i-lucide-book-marked","\u002Freference","6.reference",[159,164,169,174,179,184],{"title":160,"path":161,"stem":162,"icon":163},"Data Models","\u002Freference\u002Fdata-models","6.reference\u002F1.data-models","i-lucide-database",{"title":165,"path":166,"stem":167,"icon":168},"Activity Tracking","\u002Freference\u002Factivity-tracking","6.reference\u002F2.activity-tracking","i-lucide-activity",{"title":170,"path":171,"stem":172,"icon":173},"Testing","\u002Freference\u002Ftesting","6.reference\u002F3.testing","i-lucide-flask-conical",{"title":175,"path":176,"stem":177,"icon":178},"Observability","\u002Freference\u002Fobservability","6.reference\u002F4.observability","i-lucide-eye",{"title":180,"path":181,"stem":182,"icon":183},"Metrics Reference","\u002Freference\u002Fmetrics-reference","6.reference\u002F5.metrics-reference","i-lucide-chart-bar",{"title":185,"path":186,"stem":187,"icon":188},"Log Events Reference","\u002Freference\u002Flog-events-reference","6.reference\u002F6.log-events-reference","i-lucide-scroll-text",{"id":190,"title":29,"body":191,"description":1159,"extension":1160,"links":1161,"meta":1162,"navigation":1163,"path":30,"seo":1164,"stem":31,"__hash__":1165},"docs\u002F1.getting-started\u002F5.background-maintenance.md",{"type":192,"value":193,"toc":1141},"minimark",[194,198,213,268,271,276,281,284,418,437,444,448,451,534,540,555,559,564,577,584,671,682,693,697,704,711,730,733,737,740,770,773,777,780,800,804,807,824,827,831,847,1000,1003,1010,1014,1060,1064,1067,1081,1088,1092,1109,1113,1137],[195,196,197],"p",{},"OutlabsAuth periodically cleans up expired tokens, aggregates optional activity\nmetrics, and syncs Redis-backed API-key usage. In production, keep that work out\nof FastAPI workers.",[199,200,201],"note",{},[195,202,203,204,212],{},"The production ownership rule is: ",[205,206,207,208],"strong",{},"one scheduler clock → one one-shot worker\ninvocation → ",[209,210,211],"code",{},"run_maintenance_once()",".",[214,215,216,229],"table",{},[217,218,219],"thead",{},[220,221,222,226],"tr",{},[223,224,225],"th",{},"Component",[223,227,228],{},"Responsibility",[230,231,232,244,252,260],"tbody",{},[220,233,234,238],{},[235,236,237],"td",{},"API \u002F web processes",[235,239,240,241],{},"Serve requests with ",[209,242,243],{},"background_job_mode=\"disabled\"",[220,245,246,249],{},[235,247,248],{},"Host scheduler",[235,250,251],{},"Decide when work is due and optionally enqueue a task",[220,253,254,257],{},[235,255,256],{},"Worker or one-shot job",[235,258,259],{},"Initialize OutlabsAuth and run one maintenance cycle",[220,261,262,265],{},[235,263,264],{},"OutlabsAuth",[235,266,267],{},"Perform the enabled cleanup and sync steps",[195,269,270],{},"The scheduler and executor may share a machine or run separately. A locally\nsupervised worker is a normal production choice. Put it in the cloud only when\navailability or network access requires that placement. The executor needs\ndirect access to the host Postgres database and, when configured, Redis.",[272,273,275],"h2",{"id":274},"choose-an-entry-point","Choose an entry point",[277,278,280],"h3",{"id":279},"cli","CLI",[195,282,283],{},"Use the CLI from Cron, a systemd timer, a Kubernetes CronJob, or another\none-shot runner:",[285,286,292],"pre",{"className":287,"code":288,"filename":289,"language":290,"meta":291,"style":291},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","export DATABASE_URL='postgresql+asyncpg:\u002F\u002Fauth_worker:...@db-host\u002Fapp'\nexport OUTLABS_AUTH_SCHEMA='outlabs_auth'\nexport SECRET_KEY='...'\n\n# Required only when this host uses Redis-backed auth features:\nexport REDIS_URL='redis:\u002F\u002Fcache-host:6379\u002F0'\nexport OUTLABS_AUTH_REDIS_KEY_PREFIX='myapp:production'\n\noutlabs-auth run-maintenance\n","Terminal","bash","",[209,293,294,321,338,355,362,369,386,403,408],{"__ignoreMap":291},[295,296,299,303,307,311,314,318],"span",{"class":297,"line":298},"line",1,[295,300,302],{"class":301},"spNyl","export",[295,304,306],{"class":305},"sTEyZ"," DATABASE_URL",[295,308,310],{"class":309},"sMK4o","=",[295,312,313],{"class":309},"'",[295,315,317],{"class":316},"sfazB","postgresql+asyncpg:\u002F\u002Fauth_worker:...@db-host\u002Fapp",[295,319,320],{"class":309},"'\n",[295,322,324,326,329,331,333,336],{"class":297,"line":323},2,[295,325,302],{"class":301},[295,327,328],{"class":305}," OUTLABS_AUTH_SCHEMA",[295,330,310],{"class":309},[295,332,313],{"class":309},[295,334,335],{"class":316},"outlabs_auth",[295,337,320],{"class":309},[295,339,341,343,346,348,350,353],{"class":297,"line":340},3,[295,342,302],{"class":301},[295,344,345],{"class":305}," SECRET_KEY",[295,347,310],{"class":309},[295,349,313],{"class":309},[295,351,352],{"class":316},"...",[295,354,320],{"class":309},[295,356,358],{"class":297,"line":357},4,[295,359,361],{"emptyLinePlaceholder":360},true,"\n",[295,363,365],{"class":297,"line":364},5,[295,366,368],{"class":367},"sHwdD","# Required only when this host uses Redis-backed auth features:\n",[295,370,372,374,377,379,381,384],{"class":297,"line":371},6,[295,373,302],{"class":301},[295,375,376],{"class":305}," REDIS_URL",[295,378,310],{"class":309},[295,380,313],{"class":309},[295,382,383],{"class":316},"redis:\u002F\u002Fcache-host:6379\u002F0",[295,385,320],{"class":309},[295,387,389,391,394,396,398,401],{"class":297,"line":388},7,[295,390,302],{"class":301},[295,392,393],{"class":305}," OUTLABS_AUTH_REDIS_KEY_PREFIX",[295,395,310],{"class":309},[295,397,313],{"class":309},[295,399,400],{"class":316},"myapp:production",[295,402,320],{"class":309},[295,404,406],{"class":297,"line":405},8,[295,407,361],{"emptyLinePlaceholder":360},[295,409,411,415],{"class":297,"line":410},9,[295,412,414],{"class":413},"sBMFI","outlabs-auth",[295,416,417],{"class":316}," run-maintenance\n",[195,419,420,421,424,425,428,429,432,433,436],{},"The command initializes OutlabsAuth with background loops disabled, runs one\ncycle, and prints a typed JSON report. It exits ",[209,422,423],{},"0"," only when ",[209,426,427],{},"ok=true",", exits\n",[209,430,431],{},"1"," when a configured step is missing or reports errors, and retains Click's\nexit ",[209,434,435],{},"2"," for missing required configuration. Keep secrets in the executor\nenvironment or secret store—never in command arguments or schedule payloads.",[195,438,439,440,443],{},"The CLI constructs the standard ",[209,441,442],{},"SimpleRBAC"," maintenance configuration. If the\nhost has custom feature flags or service wiring, use the programmatic entry\npoint so the worker and API share the same auth factory.",[277,445,447],{"id":446},"programmatic","Programmatic",[195,449,450],{},"Call the deterministic one-shot API from a queue task or host-owned worker:",[285,452,457],{"className":453,"code":454,"filename":455,"language":456,"meta":291,"style":291},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","async def run_auth_maintenance():\n    auth = build_auth(background_job_mode=\"disabled\")\n    try:\n        await auth.initialize()\n        report = await auth.run_maintenance_once()\n        if not report.ok:\n            raise RuntimeError(\n                f\"auth maintenance incomplete: \"\n                f\"missing={report.missing_steps!r} \"\n                f\"errors={report.reported_errors}\"\n            )\n        return report\n    finally:\n        await auth.shutdown()\n","maintenance.py","python",[209,458,459,464,469,474,479,484,489,494,499,504,510,516,522,528],{"__ignoreMap":291},[295,460,461],{"class":297,"line":298},[295,462,463],{},"async def run_auth_maintenance():\n",[295,465,466],{"class":297,"line":323},[295,467,468],{},"    auth = build_auth(background_job_mode=\"disabled\")\n",[295,470,471],{"class":297,"line":340},[295,472,473],{},"    try:\n",[295,475,476],{"class":297,"line":357},[295,477,478],{},"        await auth.initialize()\n",[295,480,481],{"class":297,"line":364},[295,482,483],{},"        report = await auth.run_maintenance_once()\n",[295,485,486],{"class":297,"line":371},[295,487,488],{},"        if not report.ok:\n",[295,490,491],{"class":297,"line":388},[295,492,493],{},"            raise RuntimeError(\n",[295,495,496],{"class":297,"line":405},[295,497,498],{},"                f\"auth maintenance incomplete: \"\n",[295,500,501],{"class":297,"line":410},[295,502,503],{},"                f\"missing={report.missing_steps!r} \"\n",[295,505,507],{"class":297,"line":506},10,[295,508,509],{},"                f\"errors={report.reported_errors}\"\n",[295,511,513],{"class":297,"line":512},11,[295,514,515],{},"            )\n",[295,517,519],{"class":297,"line":518},12,[295,520,521],{},"        return report\n",[295,523,525],{"class":297,"line":524},13,[295,526,527],{},"    finally:\n",[295,529,531],{"class":297,"line":530},14,[295,532,533],{},"        await auth.shutdown()\n",[195,535,536,539],{},[209,537,538],{},"build_auth()"," is host code. It should select the same preset, schema, Redis\nprefix, and feature flags as the API. It must not start embedded loops.",[541,542,543],"caution",{},[195,544,545,548,549,552,553,212],{},[209,546,547],{},"\"taskq\"",", ",[209,550,551],{},"\"cron\"",", and similar values are not OutlabsAuth modes. Those\nschedulers call the one-shot API while the library remains in\n",[209,554,243],{},[272,556,558],{"id":557},"understand-one-cycle","Understand one cycle",[195,560,561,563],{},[209,562,211],{}," runs the applicable steps in this order:",[565,566,567,571,574],"ol",{},[568,569,570],"li",{},"expired and revoked refresh-token cleanup, when enabled;",[568,572,573],{},"activity aggregation, when activity tracking is enabled;",[568,575,576],{},"API-key usage sync, when the API-key service and Redis are available.",[195,578,579,580,583],{},"The returned immutable ",[209,581,582],{},"MaintenanceReport"," makes operational success explicit:",[214,585,586,596],{},[217,587,588],{},[220,589,590,593],{},[223,591,592],{},"Field",[223,594,595],{},"Meaning",[230,597,598,608,618,628,638,651,661],{},[220,599,600,605],{},[235,601,602],{},[209,603,604],{},"ok",[235,606,607],{},"No configured step is missing and no completed step reported errors",[220,609,610,615],{},[235,611,612],{},[209,613,614],{},"expected_steps",[235,616,617],{},"Steps implied by the Auth configuration",[220,619,620,625],{},[235,621,622],{},[209,623,624],{},"completed_steps",[235,626,627],{},"Steps present in this invocation's result",[220,629,630,635],{},[235,631,632],{},[209,633,634],{},"missing_steps",[235,636,637],{},"Configured steps that did not run",[220,639,640,645],{},[235,641,642],{},[209,643,644],{},"error_steps",[235,646,647,648],{},"Completed steps whose result contains ",[209,649,650],{},"errors > 0",[220,652,653,658],{},[235,654,655],{},[209,656,657],{},"reported_errors",[235,659,660],{},"Sum of per-step error counts",[220,662,663,668],{},[235,664,665],{},[209,666,667],{},"results",[235,669,670],{},"Aggregate per-step results for telemetry",[195,672,673,674,677,678,681],{},"Redis-enabled Auth expects ",[209,675,676],{},"api_key_usage_sync",", so unavailable configured\nRedis is reported as missing instead of looking like an empty success.\n",[209,679,680],{},"run_background_jobs_once()"," remains available as a backward-compatible raw\ndictionary.",[199,683,684],{},[195,685,686,687,690,691,212],{},"Host queue adapters should translate ",[209,688,689],{},"report.ok=false"," into their retry outcome.\nThe packaged CLI already converts it to exit ",[209,692,431],{},[277,694,696],{"id":695},"delivery-and-retry-behavior","Delivery and retry behavior",[195,698,699,700,703],{},"A cycle is ",[205,701,702],{},"not one transaction across all three steps",". Token cleanup and\nactivity sync commit independently, while API-key sync has its own durable\nbatch\u002Freceipt flow. A later failure can leave earlier work committed.",[195,705,706,707,710],{},"Treat delivery as ",[205,708,709],{},"at least once",":",[712,713,714,717,724,727],"ul",{},[568,715,716],{},"retry a failed invocation instead of trying to roll back the whole cycle;",[568,718,719,720,723],{},"require ",[209,721,722],{},"report.ok"," instead of interpreting a missing result key as a\nsuccessful zero-count run;",[568,725,726],{},"record the exit status and typed report fields;",[568,728,729],{},"keep host wrappers idempotent and tolerant of partial progress.",[195,731,732],{},"The built-in operations are retry-safe. API-key usage sync stages Redis\ncounters and records a database receipt so a retry does not double-apply a\ncommitted batch.",[272,734,736],{"id":735},"production-safety-contract","Production safety contract",[195,738,739],{},"Before activation, verify that:",[712,741,742,745,751,754,757,760,763],{},[568,743,744],{},"exactly one logical scheduler clock owns this database and environment;",[568,746,747,748,750],{},"every API replica uses ",[209,749,243],{},";",[568,752,753],{},"the executor uses restricted runtime credentials, not migration-owner\ncredentials;",[568,755,756],{},"the executor can reach Postgres and the host Redis, when Redis is enabled;",[568,758,759],{},"secrets live in the executor environment, never in a schedule manifest;",[568,761,762],{},"the schedule starts paused, forbids overlap, and uses queue concurrency one\nunless the host has proved parallel runs safe;",[568,764,765,766,769],{},"scheduler lag, worker availability, job outcomes, and result counts are\nmonitored independently of the API ",[209,767,768],{},"\u002Fhealth"," endpoint.",[195,771,772],{},"Do not rely on the one-clock rule alone for correctness. Schedulers and queues\ncan redeliver work, so execution must retain at-least-once semantics.",[277,774,776],{"id":775},"choose-the-cadence","Choose the cadence",[195,778,779],{},"The host owns the cadence. There is no universal five-minute interval.",[195,781,782,783,786,787,548,790,793,794,797,798,212],{},"The one-shot call runs ",[205,784,785],{},"every enabled, applicable step on every invocation",".\n",[209,788,789],{},"token_cleanup_interval_hours",[209,791,792],{},"activity_sync_interval",", and\n",[209,795,796],{},"api_key_usage_sync_interval"," control embedded loops; they do not skip work\ninside ",[209,799,211],{},[277,801,803],{"id":802},"prevent-accidental-production-execution","Prevent accidental production execution",[195,805,806],{},"Pausing a schedule prevents new scheduled occurrences; it does not make the\nworker configuration safe.",[712,808,809,812,815,818,821],{},[568,810,811],{},"Use distinct database credentials, Redis prefixes, task namespaces, and\nqueues for development, staging, and production.",[568,813,814],{},"Never fall back to a production URL when a local environment variable or\ndotenv file is missing.",[568,816,817],{},"Require an explicit environment label and fail startup when it conflicts\nwith the selected queue or expected database identity.",[568,819,820],{},"Log the environment, database host\u002Fname, schema, Redis prefix, and queue at\nstartup with credentials redacted.",[568,822,823],{},"Inspect and drain stale queued jobs before attaching a worker to production.",[195,825,826],{},"A local worker can intentionally serve production. The boundary is its explicit\nconfiguration and restricted credentials, not whether it runs in a container\nor in the cloud.",[272,828,830],{"id":829},"optional-taskq-pattern","Optional TaskQ pattern",[195,832,833,840,841,843,844,846],{},[834,835,839],"a",{"href":836,"rel":837},"https:\u002F\u002Ftaskq.outlabs.io",[838],"nofollow","TaskQ"," is one possible host scheduler; it is not an\nOutlabsAuth dependency. Register a host task that calls\n",[209,842,211],{},", converts ",[209,845,689],{}," into a retry, then starts\nwith a paused source manifest:",[285,848,853],{"className":849,"code":850,"filename":851,"language":852,"meta":291,"style":291},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","version: 1\nnamespace: myapp\nsource: api-deployment\nschedules:\n  auth-maintenance:\n    display_name: OutlabsAuth deterministic maintenance\n    task: myapp.auth.maintenance\n    queue: auth_maintenance\n    interval_seconds: 300\n    catchup: fire_once\n    overlap: forbid\n    max_lateness_seconds: 900\n    state: paused\n    payload:\n      mode: all\n","schedules\u002Fauth-maintenance.yaml","yaml",[209,854,855,867,877,887,895,902,912,922,932,942,952,962,972,982,989],{"__ignoreMap":291},[295,856,857,861,863],{"class":297,"line":298},[295,858,860],{"class":859},"swJcz","version",[295,862,710],{"class":309},[295,864,866],{"class":865},"sbssI"," 1\n",[295,868,869,872,874],{"class":297,"line":323},[295,870,871],{"class":859},"namespace",[295,873,710],{"class":309},[295,875,876],{"class":316}," myapp\n",[295,878,879,882,884],{"class":297,"line":340},[295,880,881],{"class":859},"source",[295,883,710],{"class":309},[295,885,886],{"class":316}," api-deployment\n",[295,888,889,892],{"class":297,"line":357},[295,890,891],{"class":859},"schedules",[295,893,894],{"class":309},":\n",[295,896,897,900],{"class":297,"line":364},[295,898,899],{"class":859},"  auth-maintenance",[295,901,894],{"class":309},[295,903,904,907,909],{"class":297,"line":371},[295,905,906],{"class":859},"    display_name",[295,908,710],{"class":309},[295,910,911],{"class":316}," OutlabsAuth deterministic maintenance\n",[295,913,914,917,919],{"class":297,"line":388},[295,915,916],{"class":859},"    task",[295,918,710],{"class":309},[295,920,921],{"class":316}," myapp.auth.maintenance\n",[295,923,924,927,929],{"class":297,"line":405},[295,925,926],{"class":859},"    queue",[295,928,710],{"class":309},[295,930,931],{"class":316}," auth_maintenance\n",[295,933,934,937,939],{"class":297,"line":410},[295,935,936],{"class":859},"    interval_seconds",[295,938,710],{"class":309},[295,940,941],{"class":865}," 300\n",[295,943,944,947,949],{"class":297,"line":506},[295,945,946],{"class":859},"    catchup",[295,948,710],{"class":309},[295,950,951],{"class":316}," fire_once\n",[295,953,954,957,959],{"class":297,"line":512},[295,955,956],{"class":859},"    overlap",[295,958,710],{"class":309},[295,960,961],{"class":316}," forbid\n",[295,963,964,967,969],{"class":297,"line":518},[295,965,966],{"class":859},"    max_lateness_seconds",[295,968,710],{"class":309},[295,970,971],{"class":865}," 900\n",[295,973,974,977,979],{"class":297,"line":524},[295,975,976],{"class":859},"    state",[295,978,710],{"class":309},[295,980,981],{"class":316}," paused\n",[295,983,984,987],{"class":297,"line":530},[295,985,986],{"class":859},"    payload",[295,988,894],{"class":309},[295,990,992,995,997],{"class":297,"line":991},15,[295,993,994],{"class":859},"      mode",[295,996,710],{"class":309},[295,998,999],{"class":316}," all\n",[195,1001,1002],{},"The task name and payload belong to the host adapter; they are not built into\nOutlabsAuth. The scheduler only enqueues. A supervised worker—local or\nremote—executes the task and needs Postgres\u002FRedis connectivity.",[195,1004,1005,1006,1009],{},"Activating an interval schedule is normally ",[205,1007,1008],{},"from now",": the first occurrence\nbecomes due after one full interval. Use an explicit one-shot job when you need\nan immediate canary.",[272,1011,1013],{"id":1012},"activate-safely","Activate safely",[1015,1016,1018,1022,1029,1033,1036,1040,1043,1047,1053,1057],"steps",{"level":1017},"3",[277,1019,1021],{"id":1020},"prepare-the-database","Prepare the database",[195,1023,1024,1025,1028],{},"Run ",[209,1026,1027],{},"outlabs-auth doctor",", migrate, and confirm the schema is at head.",[277,1030,1032],{"id":1031},"configure-without-activating","Configure without activating",[195,1034,1035],{},"Validate the executor environment, create the schedule paused, and confirm all\nAPI replicas have embedded background jobs disabled.",[277,1037,1039],{"id":1038},"start-the-runtime","Start the runtime",[195,1041,1042],{},"Confirm the queue has no stale jobs, then start the worker and exactly one\nscheduler while the schedule remains paused.",[277,1044,1046],{"id":1045},"canary","Canary",[195,1048,1049,1050,1052],{},"Run one explicit maintenance invocation and require ",[209,1051,427],{}," in its report.",[277,1054,1056],{"id":1055},"activate-and-observe","Activate and observe",[195,1058,1059],{},"Activate the interval schedule. Verify that one occurrence reaches one worker,\nthen confirm scheduler advancement, due lag, job success, and step counts.",[272,1061,1063],{"id":1062},"roll-back","Roll back",[195,1065,1066],{},"Use a stop-first rollback:",[565,1068,1069,1072,1075,1078],{},[568,1070,1071],{},"stop the scheduler clock;",[568,1073,1074],{},"pause the schedule;",[568,1076,1077],{},"let an in-flight job finish or drain it, then stop the worker;",[568,1079,1080],{},"keep API maintenance disabled and use manual one-shot runs if necessary.",[195,1082,1083,1084,1087],{},"Only restore ",[209,1085,1086],{},"background_job_mode=\"embedded\""," temporarily when the host is\nprovably single-process. It is unsafe in a multi-replica API because every\nreplica can become a scheduler.",[272,1089,1091],{"id":1090},"test-the-host-integration","Test the host integration",[712,1093,1094,1097,1100,1103,1106],{},[568,1095,1096],{},"Run the one-shot entry point against representative Postgres and Redis and\nassert the exact expected steps.",[568,1098,1099],{},"Repeat it and simulate a retry after partial completion.",[568,1101,1102],{},"Prove one scheduled occurrence creates one worker job.",[568,1104,1105],{},"Prove API startup does not create a second maintenance owner.",[568,1107,1108],{},"Rehearse pause, drain, and manual one-shot rollback before production.",[272,1110,1112],{"id":1111},"related","Related",[712,1114,1115,1119,1123,1127,1131],{},[568,1116,1117],{},[834,1118,46],{"href":47},[568,1120,1121],{},[834,1122,24],{"href":25},[568,1124,1125],{},[834,1126,170],{"href":171},[568,1128,1129],{},[834,1130,175],{"href":176},[568,1132,1133],{},[834,1134,1136],{"href":836,"rel":1135},[838],"TaskQ documentation",[1138,1139,1140],"style",{},"html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":291,"searchDepth":298,"depth":323,"links":1142},[1143,1147,1150,1154,1155,1156,1157,1158],{"id":274,"depth":323,"text":275,"children":1144},[1145,1146],{"id":279,"depth":340,"text":280},{"id":446,"depth":340,"text":447},{"id":557,"depth":323,"text":558,"children":1148},[1149],{"id":695,"depth":340,"text":696},{"id":735,"depth":323,"text":736,"children":1151},[1152,1153],{"id":775,"depth":340,"text":776},{"id":802,"depth":340,"text":803},{"id":829,"depth":323,"text":830},{"id":1012,"depth":323,"text":1013},{"id":1062,"depth":323,"text":1063},{"id":1090,"depth":323,"text":1091},{"id":1111,"depth":323,"text":1112},"Run cleanup and sync work safely outside FastAPI processes.","md",null,{},{"icon":32},{"title":29,"description":1159},"AcvH0bHJ8ln3j1joThR9TMAurHRW3i5ixwM4ap0wPoM",[1167,1169],{"title":24,"path":25,"stem":26,"description":1168,"icon":27,"children":-1},"Production migrate, Redis, and multi-worker checklist.",{"title":41,"path":42,"stem":43,"description":1170,"icon":44,"children":-1},"Which get_*_router factories to mount and how prefixes work.",1787472745731]