description: "Shared Loader boot support for dsh profiles and the temporary Python SDK runtime: environment layers, patches, diagnostics, and configuration preview."
English | 中文
dsh-app-boot is the shared Loader boot library behind dsh profiles, including the CLI packaged by the Python runtime wheel. It loads environment layers, composes profile bundles and patches, boots every plugin, and returns the running app or identifies the failed plugin and cause. Product applications use the dsh launcher instead of publishing separate bins; direct-config helpers remain only for lower-level embedders and tests. You can preview the effective configuration before booting, select live or startup-only patch application per profile, and let a terminal-owning app restore its terminal before a fatal exit.
Starting an app with this package is a small, explicit entry point: you give it a config file and it runs the whole boot. This section covers what you can do and what you get; the helper calls behind each outcome are documented in the folded implementation section.
Use it when implementing the shared dsh launcher or embedding its lower-level boot helpers. Product features belong in profile bundles instead of new application bins; code that only adds plugins to an already-running app mounts those plugins directly.
You give your entry point a config file, and the process starts the whole app: it loads your environment layers, applies patches and profiles, boots every plugin, and returns once the app is running. In replay mode it boots the sibling cordis.snapshot.yml instead, so a recorded session reproduces identically. The smallest entry point is two calls:
installFailLoud('dsh')
const ctx = await boot('dsh', resolveConfigPath(argv[2], process.env.DSH_SNAPSHOT))
With that entry point, startup keeps every plugin that can activate. An enabled failed plugin produces a labelled warning. A failed required entry makes startup dispose the whole app and exit nonzero; required ids absent from a profile and disabled required entries do not affect startup. The global required list covers shared Agent execution, application endpoints, and Web bootstrap/transport: agent-loop, webserver, modules, connection, headless-runner, acp, and sdk-jsonrpc-server.
Import profile and bundle declaration types from @deepseek-ai/dsh-package-manifest. App-boot adapts DshPackageManifest to ProfileManifest with optional package identity because local profiles need no published version. App-boot owns profile loading, JSON validation, and resolved runtime data.
A profile is how one dsh installation ships different app surfaces: web, headless, acp, sdk, and sdk-minimal start distinct compositions from the same launcher. A profile lives at $DSH_HOME/profiles/<name> and combines installable bundles, its own cordis.patch.yml, and patchReload: live | startup; omitted reload policy keeps the historical live default for custom profiles. The shipped web template uses live reload, while the other shipped templates apply patches only at startup. sdk-minimal names only its standalone bundle; the other templates retain base-plus-mode stacks. dsh --profile <name> --from-default-profile <template> creates a custom profile at a new non-shipped name from one shipped template, while dsh plugin initializes a base-backed profile and manages its installed bundles. A missing bundle or one without a patch declaration fails startup loudly. Application-owned npm projects, such as Electron's reserved Desktop profile, use loadProfileDirectory to load an already initialized directory without exposing it through CLI profile lookup.
Your machine-local preferences also live in the Harness home:
.env — your ordinary environment layers: the invoking directory's file outranks the Harness-home file, and both sit below the inherited environment. Variables that decide how the process starts (PATH, DSH_*, XDG_* and similar) are rejected from files: export them instead. The four proxy names (HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, NO_PROXY) are accepted from the Harness-home file only, never from the invoking directory's, which arrives with a clone. For a non-product bin that just wants one directory's .env, a missing file is fine and an unloadable one prints one labelled warning line.cordis.patch.yml — your tweak layer, applied after every bundle layer (per-profile first, then the home-level file, which therefore outranks it): replace one entry's whole config (restating the fields you keep), insert new entries, or interpolate !!js expressions at boot. A patch naming an entry that does not exist prints a stderr warning; an empty or comments-only file fails boot — disable the layer with [] instead.Profiles with patchReload: live watch both user patch files and apply the reload failure policy. A startup profile installs neither those watchers nor the launcher's watch-only HMR fallback.
Inserted plugin names may be absolute filesystem paths, file URLs, or package specifiers. Patch loading converts absolute paths and patch-relative ./ or ../ paths to file URLs within insert rows and their nested groups; existing-entry name assertions and replacement config values remain literal.
Before you boot, you can print the exact configuration the app will mount: the dump shows the composed entry list with !!js expressions verbatim, grouped under comments naming each source file and the patch layers that changed it, as one loadable YAML document. Patches that match no row are reported with their layer label; a missing, unparsable, or invalid config fails the dump.
After the Loader settles, app-boot reports optional failures as warnings and rejects startup if an enabled required entry cannot activate. In the table, stopping startup means disposing any mounted plugins and exiting nonzero without reporting readiness; continuing keeps successful plugins running. Later configuration HMR does not repeat the required-startup audit and does not roll back the whole update.
| Failure pattern | Optional entry at startup | Required entry at startup | Later configuration HMR |
|---|---|---|---|
| Root config or required overlay is missing, unreadable, malformed, or contains invalid entries | Stop startup | Stop startup | Malformed or invalid live patches are rejected without changing the running configuration; a valid edit applies |
| Module import fails or module evaluation throws | Warn; continue | Stop startup | Report the error; keep successful siblings; a corrected import can activate |
| Plugin config schema validation fails | Warn; continue | Stop startup | A new entry stays inactive; an existing entry retains its prior instance and config; a valid correction applies |
Config !!js evaluation throws |
Warn; continue | Stop startup | Report the error; keep successful siblings; a valid correction can activate |
disabled: !!js evaluation throws |
Warn; continue | Stop startup | Report the evaluation error rather than treating the entry as disabled; a valid correction can activate |
Synchronous apply() throws |
Warn; continue | Stop startup | Report the error; keep successful siblings; corrected config can activate |
Asynchronous apply() throws |
Warn after settlement; continue | Stop startup after settlement | Report the error after settlement; keep successful siblings; corrected config can activate |
| An injected service is unavailable | Warn; continue while the entry waits for its dependencies | Stop startup | Keep the entry waiting; adding the missing provider can activate it |
| HTTP port binding fails | Warn; continue without that endpoint | Stop startup | Keep the process running without the failed endpoint; corrected config can restore it |
Detached asynchronous work outside the apply() return Promise produces an unhandled rejection |
Fatal: dispose the app and exit nonzero | Fatal: dispose the app and exit nonzero | Fatal: dispose the app and exit nonzero, regardless of entry id |
| Entry is absent or explicitly disabled | Ignore it | Ignore it | Do not activate it; no required-startup audit |
The required list above includes modules and connection; Web startup cannot succeed when either enabled entry fails. Failure of an optional provider can also prevent a required consumer from activating. Schema rejection before an existing entry updates is not a transactional rollback of sibling changes.
The Web process matrix and startup acceptance verify these outcomes through the shipped Web profile; app-boot tests also exercise root Include failures.
If your app owns the terminal, it can hand the terminal back before the process exits, so your shell is never left in raw mode. The handoff is bounded: a stuck cleanup delays the fatal exit but never cancels it.
When your app boots a model-backed agent, you can tell the agent where the DSH implementation checkout lives: it learns that path and that it must not infer the working directory from it — it should use pwd. The instruction appears once near the top of the system prompt. Apps without a system prompt service skip it; in development, reloading the system prompt drops it until the next boot.
Read these pages when the package-level contract is not enough. They move from the shared boot mechanics to the composition model and the decision evidence behind it.
!!js config expressions, and include/group semantics.dsh bin that consumes these helpers.dsh --profile.resolveDshHome).Indirectly, through the loaded plugin tree, which alone contributes model context; the one export that adds model-visible text, addHarnessSourceSection, does so only when a consumer calls it after boot.
Boot itself changes no request prefix. addHarnessSourceSection places its source path after first-party reusable instructions, so different checkouts leave those preceding bytes unchanged when tools and configuration match. Provider cache reuse is not guaranteed.
These limits describe when this boot library is a poor fit or needs special care. They are current package constraints, not a task backlog.
cordis.yml or cordis.yaml maps to the sibling cordis.snapshot.yml; custom config names require caller-managed selection.loadLayeredEnv reads only the invocation directory and Harness home once; it does not search parents or follow a workspace selected later. loadEnv remains the one-directory helper for non-product bins.