Skip to main content
This guide covers running Celly as a long-lived service on a Windows 11 host, including Task Scheduler setup and the personal-access-token (PAT) flow for re-authenticating sbx without an interactive browser. The bot must run as the logged-in user on the host: the sbx daemon and its credentials are per-user, and only the host can invoke the sbx CLI. NSSM or a LocalSystem service will not work.

1. One-time host bootstrap

Run these steps once, in order, as the user who will own the bot. Steps 1–2 need an elevated PowerShell prompt; the rest run as the normal user.
  1. Enable the Windows Hypervisor Platform:
  2. Install sbx:
  3. Run the host prep step:
  4. Log in to Docker:
  5. Initialize the network policy preset. It is required before the first sandbox, otherwise sbx create blocks on an interactive prompt:
  6. Register provider credentials used by the sandboxed agent (see Providers):
  7. Record the installed version. Celly targets sbx >= 0.45.0:

2. Build and configure the bot

  1. Install the pinned Node version (24.x) and clone the repository.
  2. Copy .env.example to .env and fill in DISCORD_TOKEN and DISCORD_GUILD_ID. See Configuration for the full variable list.
  3. Install dependencies and build:
  4. Run the bot once in the foreground to confirm it boots:
    On boot it performs a preflight (sbx version, policy check, single-instance lock) and fails fast with an actionable message. Logs are written to data/bot.log.

3. Task Scheduler at logon

The task must run only when the user is logged on. Do not choose “Run whether user is logged on or not”: that option requires storing the account password and can start the task outside the user’s sbx session. Run the following in a normal (non-elevated) PowerShell prompt. It registers a task that starts Node with the repo as its working directory, never times out, and restarts on failure.
-ExecutionTimeLimit ([TimeSpan]::Zero) clears the default 72-hour limit so the bot is not killed. Adjust $repo to the clone location. To inspect, run, or remove the task:

Option B: Task Scheduler GUI

  1. Open Task Scheduler and choose Create Task (not “Basic Task”).
  2. General: name it Celly; select Run only when user is logged on.
  3. Triggers: add At log on, scoped to the user who owns sbx.
  4. Actions: Start a program with:
    • Program/script: the full path to node.exe (for example C:\Program Files\nodejs\node.exe).
    • Add arguments: dist\index.js.
    • Start in: the repository root.
  5. Settings: uncheck Stop the task if it runs longer than…, and enable If the task fails, restart every 1 minute (up to 3 times).
The bot’s single-instance lock prevents a second copy from double-driving sandboxes, so a logon and a manual start cannot both run.

4. PAT headless re-login

sbx normally signs in through an interactive browser. On a headless or re-login scenario (for example, the machine reboots and the user session starts without a browser, or the stored credential expires), the at-logon task starts but sbx is not authenticated. Re-authenticate non-interactively with a Docker personal access token (PAT).
  1. Create a Docker PAT: sign in at https://hub.docker.com, open Account settings → Personal access tokens, and create a token with at least read access.
  2. Feed the token to sbx over stdin with --password-stdin. Never pass the token as a command-line argument, where it would be visible in the process list and PowerShell history:
  3. Verify:
  4. If the bot is already running, restart the scheduled task so the daemon picks up the new credential:
Store the PAT the same way you store other host secrets (for example, Windows Credential Manager) if you need to automate this step; do not commit it or put it in .env.

5. Backups and log rotation

Celly writes a full SQLite backup of DATA_DIR/bot.db with VACUUM INTO and prunes old copies. It also rotates bot.log and each project server log. Backups land in data\backups\bot-<ISO>.db (for example bot-2026-09-27T00-00-00-000Z.db). To restore, stop the task, replace data\bot.db, and start the task again:
ADMIN_PORT (default 4560, 0 disables) serves a loopback-only status page and JSON API. Reach it from the host at http://127.0.0.1:4560; never port-forward it.

6. Verifying a deployment

  • sbx diagnose reports a healthy install, daemon, and authentication.
  • sbx policy ls shows the balanced preset as the floor.
  • node scripts/smoke.mjs C:\path\to\a\project\dir prints smoke OK (full chain: create → bootstrap → serve → health → create session → prompt → abort → stop → remove, with teardown on failure).
  • After an at-logon start, data/bot.log shows the preflight passing and the Discord client logging in.

7. Troubleshooting

8. Sensitive paths

PROJECTS_ROOT may live under the user profile (the default is %USERPROFILE%\Celly\projects). The sensitive-path denylist rejects specific sensitive subtrees (.ssh, .aws, .gnupg, .config, .docker, .kube, .azure, .npmrc, .netrc, .celly, AppData), DATA_DIR, the bot repository, and system directories, so keep projects out of those. See Configuration for the complete list.

9. Limitations to plan around

  • The in-memory message queue is lost on bot restart. Active runs are re-attached from session history, but queued-but-unsent prompts are dropped.
  • Role configuration accepts role IDs only, not role names.
  • The finalization token/duration footer is descoped.
  • Per-user command rate limiting, sandbox disk-usage warnings, and the DATA_DIR cloud-sync warning are backlog. Keep DATA_DIR outside any OneDrive/Dropbox-synced folder yourself.
  • The smoke script (node scripts/smoke.mjs) and live Discord interactions are host-only and are not exercised by the Linux test suite.
Deferred commands and features are listed in the commands reference and the changelog.