How surfaces boot
OpenSRE can be reached in several ways — theopensre CLI, the interactive
shell, the gateway daemon (chat channels), the gateway’s web app, scheduled
cron commands, and even a plain Python script embedding the agent. Each of
them starts up by running the same shared setup —
configure_process(<profile>) in
bootstrap/process.py — rather than assembling
its own startup sequence. A surface’s own code is responsible only for its
channel and UX (rendering to a terminal, replying in chat, serving an HTTP
route, parsing CLI arguments) plus any prerequisites specific to that
surface; some surfaces do a little of their own setup before or after the
shared steps run (see “Where each surface calls it” below).
What the shared setup does
Startup is a short checklist of setup work that has to happen in the same order every time:- Load the environment — read configuration and secrets.
- Start error reporting — so failures later in startup are still captured.
- Register adapters — connect the agent to the integrations and tools it can use.
- Register the scheduler’s task runners — so scheduled work (digests, reports, cron jobs) knows how to run.
- Log capability warnings — flag anything the sandbox can’t do in this environment.
- Preload the LLM client modules — so a long-running process doesn’t end up mixing old and new versions of those modules after a later code change.
The six profiles
Where each surface calls it
- CLI —
surfaces/cli/startup.pyruns the CLI profile first, then handles CLI-only setup: its own error reporting (tolerant of a missing dependency duringopensre update), terminal output styling, and keyboard-interrupt handling.main.py, the module-level entry point (python main.py), delegates straight into this same CLI path — it does not use the embedded profile. (The embedded profile only appears inmain.py’s docstring, as an illustrative example for someone embedding the agent in their own script.) The interactive shell runs inside this same already-started CLI process, so it doesn’t start up again. - Interactive shell’s saved loops — when a saved prompt loop needs its
own background scheduler, it starts up with the scheduled-command profile
in
surfaces/interactive_shell/runtime/loop_scheduler.pyandsurfaces/interactive_shell/command_registry/loops_cmds.py. - Gateway daemon —
gateway/core/runtime/manager.pysets up its own logging, readiness state, and credentials first, then runs the gateway profile before connecting chat channels and the scheduler. - Gateway web app —
gateway/web/webapp.pyruns the web profile as soon as the module loads, so both the in-process gateway and a standalone web server have everything they need before handling a request. - Scheduled/cron CLI commands — one-off commands like
cron runinsurfaces/cli/commands/cron.pyandsentry_digest.pyrun the scheduled-command profile.posthog_report.pychecks that the PostHog integration is configured first, then runs the same profile. The long-runningcron startdaemon uses the scheduler-worker profile instead, since it needs its own error reporting and only needs to register task runners once, not on every run. - Embedded Python usage —
bootstrap/embedded.pyruns the embedded profile. This is also the pattern to follow if you’re driving the agent from your own Python script: runconfigure_process(EMBEDDED_PROFILE)before your first request.
Why it works this way
Keeping one shared startup checklist means every surface gets the same guarantees — nothing is skipped, and nothing runs out of order — no matter how it’s launched. It also means adding or changing a startup step only needs to happen in one place. For how this fits into OpenSRE’s broader package layout, seedocs/ARCHITECTURE.md.