-
Notifications
You must be signed in to change notification settings - Fork 1.3k
fix(lifecycle): align SDK validation and hook coverage #1605
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Pangjiping
merged 4 commits into
opensandbox-group:main
from
jianpingpei:fix/lifecycle-hooks-followups
Aug 24, 2026
Merged
Changes from 1 commit
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
d98d29a
fix(lifecycle): align SDK validation and hook coverage
jianpingpei 7b9b4c0
docs(lifecycle): make hook examples runnable
jianpingpei 0bb3fec
test(execd): clarify lifecycle barrier comment
jianpingpei 0884795
Merge branch 'main' into fix/lifecycle-hooks-followups
Pangjiping File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| --- | ||
| title: Lifecycle Hooks | ||
| description: Run setup work before a sandbox entrypoint and schedule recurring work while it is running. | ||
| --- | ||
|
|
||
| # Lifecycle Hooks | ||
|
|
||
| Lifecycle hooks let a sandbox run declarative commands at defined points in its runtime. OpenSandbox currently supports a blocking `preStart` hook and one or more non-blocking `periodic` hooks. | ||
|
|
||
| ## Supported hooks | ||
|
|
||
| | Hook | Cardinality | When it runs | Failure behavior | | ||
| |---|---:|---|---| | ||
| | `preStart` | Zero or one | After execd starts listening, before the user entrypoint starts | A failure or timeout prevents the user entrypoint from starting | | ||
| | `periodic` | Zero or more | The scheduler starts after `preStart` succeeds and runs each hook on its schedule | A failed or timed-out run is logged and later runs normally continue; if a timed-out process cannot be terminated, that hook is disabled | | ||
|
|
||
| Both hooks run inside the sandbox with the sandbox environment. Commands are argument arrays and are executed directly. To use shell syntax such as pipes, redirects, or variable expansion, invoke a shell explicitly, for example `['sh', '-c', 'command > file']`. | ||
|
|
||
|  | ||
|
|
||
| The ordering is the same in both startup modes: | ||
|
|
||
| 1. execd starts and its HTTP server begins listening. | ||
| 2. `preStart` runs to completion, if configured. | ||
| 3. The periodic scheduler starts, if configured. | ||
| 4. The user entrypoint starts. | ||
|
|
||
| In execd-as-init mode, execd remains PID 1 and starts the entrypoint as its supervised child. In bootstrap mode, the bootstrap process starts the entrypoint after execd reports that lifecycle startup completed. | ||
|
|
||
| ## Configuration | ||
|
|
||
| Lifecycle hooks are part of the sandbox creation request: | ||
|
|
||
| ```json | ||
| { | ||
| "image": { "uri": "ubuntu:24.04" }, | ||
| "entrypoint": ["tail", "-f", "/dev/null"], | ||
| "resourceLimits": { | ||
| "cpu": "1", | ||
| "memory": "1Gi" | ||
| }, | ||
| "lifecycle": { | ||
| "preStart": { | ||
| "command": ["sh", "-c", "/opt/hooks/restore.sh"], | ||
|
Pangjiping marked this conversation as resolved.
Outdated
|
||
| "timeoutSeconds": 120 | ||
| }, | ||
| "periodic": [ | ||
| { | ||
| "name": "checkpoint", | ||
| "schedule": "@every 5m", | ||
| "command": ["sh", "-c", "/opt/hooks/checkpoint.sh"], | ||
| "timeoutSeconds": 120 | ||
| } | ||
| ] | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### `preStart` | ||
|
|
||
| | Field | Required | Description | | ||
| |---|---:|---| | ||
| | `command` | Yes | Non-empty command and argument array | | ||
| | `timeoutSeconds` | No | Server-validated timeout from 1 through 300 seconds; defaults to 60 seconds | | ||
|
Pangjiping marked this conversation as resolved.
Outdated
|
||
|
|
||
| `preStart` runs on each container start. Make the command idempotent so retrying or restarting a sandbox does not corrupt its state. | ||
|
|
||
| ### `periodic` | ||
|
|
||
| | Field | Required | Description | | ||
| |---|---:|---| | ||
| | `name` | Yes | Non-blank name, unique within the sandbox | | ||
| | `schedule` | Yes | Standard five-field cron expression or descriptor such as `@hourly` or `@every 30s` | | ||
| | `command` | Yes | Non-empty command and argument array | | ||
| | `timeoutSeconds` | No | Server-validated timeout from 1 through 300 seconds; defaults to 60 seconds | | ||
|
|
||
| An `@every` interval must be a whole number of seconds and at least one second. Runs of the same named hook never overlap: if the previous run is still active, the next scheduled run is skipped. | ||
|
|
||
| ## Current availability | ||
|
|
||
| Lifecycle hooks currently require the Kubernetes provider. They are rejected by the Docker provider and Fleets backend. A request cannot combine `lifecycle` with `poolRef`. | ||
|
|
||
| The SDKs expose lifecycle fields on their sandbox creation APIs but do not enforce the timeout range. The Server is the authority for request validation. See the language-specific examples in the [SDK documentation](/sdks/). | ||
|
|
||
| ## Operational guidance | ||
|
|
||
| - Keep `preStart` bounded and deterministic because the entrypoint waits for it. | ||
| - Make periodic work idempotent and safe to retry. | ||
| - Store credentials through the platform's supported secret mechanism rather than embedding them in commands. | ||
| - Write data that must survive node replacement to durable storage. Lifecycle hooks schedule recovery and synchronization work; they do not make the sandbox filesystem durable by themselves. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <mxfile host="claude-code" agent="claude-code-drawio-skill" version="24.7.17" type="device"> | ||
| <diagram name="Lifecycle hook timing" id="lifecycle-hook-timing"> | ||
| <mxGraphModel dx="1200" dy="640" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1200" pageHeight="640" math="0" shadow="0"> | ||
| <root> | ||
| <mxCell id="0"/> | ||
| <mxCell id="1" parent="0"/> | ||
|
|
||
| <mxCell id="title" value="Lifecycle hook timing" style="text;html=1;align=left;verticalAlign=middle;fontFamily=Helvetica;fontSize=24;fontStyle=1;fontColor=#0F172A" vertex="1" parent="1"> | ||
| <mxGeometry x="60" y="32" width="500" height="34" as="geometry"/> | ||
| </mxCell> | ||
| <mxCell id="subtitle" value="preStart is a startup gate; periodic hooks begin only after startup succeeds." style="text;html=1;align=left;verticalAlign=middle;fontFamily=Helvetica;fontSize=13;fontColor=#64748B" vertex="1" parent="1"> | ||
| <mxGeometry x="60" y="68" width="760" height="24" as="geometry"/> | ||
| </mxCell> | ||
|
|
||
| <mxCell id="startup-dot" value="" style="ellipse;html=1;aspect=fixed;fillColor=#2563EB;strokeColor=none" vertex="1" parent="1"> | ||
| <mxGeometry x="60" y="113" width="10" height="10" as="geometry"/> | ||
| </mxCell> | ||
| <mxCell id="startup-label" value="Sandbox startup" style="text;html=1;align=left;verticalAlign=middle;fontFamily=Helvetica;fontSize=14;fontStyle=1;fontColor=#334155" vertex="1" parent="1"> | ||
| <mxGeometry x="82" y="105" width="240" height="26" as="geometry"/> | ||
| </mxCell> | ||
|
|
||
| <mxCell id="container-starts" value="Container starts" style="rounded=1;arcSize=12;html=1;whiteSpace=wrap;fillColor=#FFFFFF;strokeColor=#94A3B8;strokeWidth=1.5;fontFamily=Helvetica;fontSize=14;fontColor=#0F172A" vertex="1" parent="1"> | ||
| <mxGeometry x="60" y="150" width="150" height="72" as="geometry"/> | ||
| </mxCell> | ||
| <mxCell id="execd-ready" value="execd ready" style="rounded=1;arcSize=12;html=1;whiteSpace=wrap;fillColor=#FFFFFF;strokeColor=#64748B;strokeWidth=1.5;fontFamily=Helvetica;fontSize=14;fontColor=#0F172A" vertex="1" parent="1"> | ||
| <mxGeometry x="270" y="150" width="150" height="72" as="geometry"/> | ||
| </mxCell> | ||
| <mxCell id="prestart" value="<b>preStart</b><br><font color="#92400E" size="2">startup gate</font>" style="rounded=1;arcSize=12;html=1;whiteSpace=wrap;fillColor=#FFF7ED;strokeColor=#F59E0B;strokeWidth=1.5;fontFamily=Helvetica;fontSize=15;fontColor=#78350F" vertex="1" parent="1"> | ||
| <mxGeometry x="480" y="150" width="150" height="72" as="geometry"/> | ||
| </mxCell> | ||
| <mxCell id="entrypoint-starts" value="User entrypoint starts" style="rounded=1;arcSize=12;html=1;whiteSpace=wrap;fillColor=#FFFFFF;strokeColor=#64748B;strokeWidth=1.5;fontFamily=Helvetica;fontSize=14;fontColor=#0F172A" vertex="1" parent="1"> | ||
| <mxGeometry x="690" y="150" width="180" height="72" as="geometry"/> | ||
| </mxCell> | ||
| <mxCell id="workload-running" value="User workload running" style="rounded=1;arcSize=12;html=1;whiteSpace=wrap;fillColor=#F0FDF4;strokeColor=#22C55E;strokeWidth=1.5;fontFamily=Helvetica;fontSize=14;fontStyle=1;fontColor=#166534" vertex="1" parent="1"> | ||
| <mxGeometry x="930" y="150" width="210" height="72" as="geometry"/> | ||
| </mxCell> | ||
|
|
||
| <mxCell id="startup-failed" value="<b>Failure or timeout</b><br><font color="#991B1B" size="2">entrypoint is not started</font>" style="rounded=1;arcSize=12;html=1;whiteSpace=wrap;fillColor=#FEF2F2;strokeColor=#EF4444;strokeWidth=1.5;fontFamily=Helvetica;fontSize=14;fontColor=#991B1B" vertex="1" parent="1"> | ||
| <mxGeometry x="455" y="280" width="200" height="72" as="geometry"/> | ||
| </mxCell> | ||
|
|
||
| <mxCell id="divider" value="" style="rounded=0;html=1;fillColor=#E2E8F0;strokeColor=none" vertex="1" parent="1"> | ||
| <mxGeometry x="60" y="390" width="1080" height="1" as="geometry"/> | ||
| </mxCell> | ||
| <mxCell id="running-dot" value="" style="ellipse;html=1;aspect=fixed;fillColor=#16A34A;strokeColor=none" vertex="1" parent="1"> | ||
| <mxGeometry x="60" y="427" width="10" height="10" as="geometry"/> | ||
| </mxCell> | ||
| <mxCell id="running-label" value="While the sandbox is running" style="text;html=1;align=left;verticalAlign=middle;fontFamily=Helvetica;fontSize=14;fontStyle=1;fontColor=#334155" vertex="1" parent="1"> | ||
| <mxGeometry x="82" y="419" width="320" height="26" as="geometry"/> | ||
| </mxCell> | ||
|
|
||
| <mxCell id="scheduler" value="Periodic scheduler" style="rounded=1;arcSize=12;html=1;whiteSpace=wrap;fillColor=#FFFFFF;strokeColor=#64748B;strokeWidth=1.5;fontFamily=Helvetica;fontSize=14;fontColor=#0F172A" vertex="1" parent="1"> | ||
| <mxGeometry x="60" y="475" width="190" height="64" as="geometry"/> | ||
| </mxCell> | ||
| <mxCell id="periodic-1" value="periodic" style="rounded=1;arcSize=12;html=1;whiteSpace=wrap;fillColor=#F0FDF4;strokeColor=#16A34A;strokeWidth=1.5;fontFamily=Helvetica;fontSize=14;fontStyle=1;fontColor=#166534" vertex="1" parent="1"> | ||
| <mxGeometry x="330" y="475" width="140" height="64" as="geometry"/> | ||
| </mxCell> | ||
| <mxCell id="periodic-2" value="periodic" style="rounded=1;arcSize=12;html=1;whiteSpace=wrap;fillColor=#F0FDF4;strokeColor=#16A34A;strokeWidth=1.5;fontFamily=Helvetica;fontSize=14;fontStyle=1;fontColor=#166534" vertex="1" parent="1"> | ||
| <mxGeometry x="550" y="475" width="140" height="64" as="geometry"/> | ||
| </mxCell> | ||
| <mxCell id="periodic-3" value="periodic" style="rounded=1;arcSize=12;html=1;whiteSpace=wrap;fillColor=#F0FDF4;strokeColor=#16A34A;strokeWidth=1.5;fontFamily=Helvetica;fontSize=14;fontStyle=1;fontColor=#166534" vertex="1" parent="1"> | ||
| <mxGeometry x="770" y="475" width="140" height="64" as="geometry"/> | ||
| </mxCell> | ||
| <mxCell id="continuation" value="…" style="ellipse;html=1;aspect=fixed;fillColor=#F8FAFC;strokeColor=#94A3B8;strokeWidth=1.5;fontFamily=Helvetica;fontSize=22;fontColor=#475569" vertex="1" parent="1"> | ||
| <mxGeometry x="990" y="475" width="64" height="64" as="geometry"/> | ||
| </mxCell> | ||
|
|
||
| <mxCell id="mode-note" value="Bootstrap mode and execd-as-init mode expose the same hook timing." style="text;html=1;align=center;verticalAlign=middle;fontFamily=Helvetica;fontSize=12;fontColor=#64748B" vertex="1" parent="1"> | ||
| <mxGeometry x="60" y="580" width="1080" height="24" as="geometry"/> | ||
| </mxCell> | ||
|
|
||
| <mxCell id="e-container-execd" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#64748B;strokeWidth=2;endArrow=block;endFill=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0" edge="1" parent="1" source="container-starts" target="execd-ready"> | ||
| <mxGeometry relative="1" as="geometry"> | ||
| <Array as="points"><mxPoint x="240" y="186"/></Array> | ||
| </mxGeometry> | ||
| </mxCell> | ||
| <mxCell id="e-execd-prestart" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#64748B;strokeWidth=2;endArrow=block;endFill=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0" edge="1" parent="1" source="execd-ready" target="prestart"> | ||
| <mxGeometry relative="1" as="geometry"> | ||
| <Array as="points"><mxPoint x="450" y="186"/></Array> | ||
| </mxGeometry> | ||
| </mxCell> | ||
| <mxCell id="e-prestart-entrypoint" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#64748B;strokeWidth=2;endArrow=block;endFill=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0" edge="1" parent="1" source="prestart" target="entrypoint-starts"> | ||
| <mxGeometry relative="1" as="geometry"> | ||
| <Array as="points"><mxPoint x="660" y="186"/></Array> | ||
| </mxGeometry> | ||
| </mxCell> | ||
| <mxCell id="e-entrypoint-workload" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#64748B;strokeWidth=2;endArrow=block;endFill=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0" edge="1" parent="1" source="entrypoint-starts" target="workload-running"> | ||
| <mxGeometry relative="1" as="geometry"> | ||
| <Array as="points"><mxPoint x="900" y="186"/></Array> | ||
| </mxGeometry> | ||
| </mxCell> | ||
| <mxCell id="e-prestart-failure" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#DC2626;strokeWidth=2;endArrow=block;endFill=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0" edge="1" parent="1" source="prestart" target="startup-failed"> | ||
| <mxGeometry relative="1" as="geometry"> | ||
| <Array as="points"><mxPoint x="555" y="250"/></Array> | ||
| </mxGeometry> | ||
| </mxCell> | ||
| <mxCell id="e-scheduler-periodic-1" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#16A34A;strokeWidth=2;endArrow=block;endFill=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0" edge="1" parent="1" source="scheduler" target="periodic-1"> | ||
| <mxGeometry relative="1" as="geometry"> | ||
| <Array as="points"><mxPoint x="290" y="507"/></Array> | ||
| </mxGeometry> | ||
| </mxCell> | ||
| <mxCell id="e-periodic-1-2" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#16A34A;strokeWidth=2;endArrow=block;endFill=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0" edge="1" parent="1" source="periodic-1" target="periodic-2"> | ||
| <mxGeometry relative="1" as="geometry"> | ||
| <Array as="points"><mxPoint x="510" y="507"/></Array> | ||
| </mxGeometry> | ||
| </mxCell> | ||
| <mxCell id="e-periodic-2-3" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#16A34A;strokeWidth=2;endArrow=block;endFill=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0" edge="1" parent="1" source="periodic-2" target="periodic-3"> | ||
| <mxGeometry relative="1" as="geometry"> | ||
| <Array as="points"><mxPoint x="730" y="507"/></Array> | ||
| </mxGeometry> | ||
| </mxCell> | ||
| <mxCell id="e-periodic-3-continuation" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#16A34A;strokeWidth=2;endArrow=block;endFill=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0" edge="1" parent="1" source="periodic-3" target="continuation"> | ||
| <mxGeometry relative="1" as="geometry"> | ||
| <Array as="points"><mxPoint x="950" y="507"/></Array> | ||
| </mxGeometry> | ||
| </mxCell> | ||
| </root> | ||
| </mxGraphModel> | ||
| </diagram> | ||
| </mxfile> |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.