# Box Job Lifecycle over HTTPS (No SSH) > **Box is the main surface.** All operator work goes through Box (box.muse-dev.online). The web UI, `box` CLI, and agents share the same API endpoints. No UI-only powers. **Date:** 2026-10-06 **Status:** implemented on bl (`exec-constrained.py` + `box-relay.sh`; `box-ctl.py` verbs pre-existed) **Scope:** safe job-lifecycle mutations. Deletes are deliberately NOT exposed (`job-delete`, `timer-delete` stay SSH/operator-only). ## 1. Why Agents own scheduled automation but could only run jobs (`cron.run`) or read them (`box.exec`). Creating, updating, triggering, chaining, previewing, and pausing jobs needed SSH. All of this now rides the agent HTTPS path (`https://exec.muse-dev.online/exec`, signature or bearer auth, named-op allowlist, audit log). ## 2. New ops | Op | Args | Backend | Write? | Who | |---|---|---|---|---| | `job.put` | `{name, definition}` | `box-ctl.py job-put` (definition on stdin) | yes (writes + commits) | known identities only | | `job.trigger` | `{name}` | `box-ctl.py job-trigger` | yes (dispatches now) | known identities only | | `job.chain` | `{from, to, on_failure?}` | `box-ctl.py job-chain` | yes (writes + commits) | known identities only | | `job.next` | `{job_id, success?}` | `box-ctl.py job-next` | no (dry-run) | any valid signer | | `cron.timer_stop` | `{name}` | `box-ctl.py timer-stop` | yes (systemd) | known identities only | | `cron.timer_disable` | `{name}` | `box-ctl.py timer-disable` | yes (systemd) | known identities only | New `box-relay.sh` client commands: ```bash box cron put '' # create or update (see ยง3) box cron trigger # dispatch now (audited JSON) box cron chain [--on-failure] # wire chain_next box cron next [--success|--fail] # dry-run: what dispatches next box timer stop # pause schedule (keeps unit) box timer disable # pause schedule (disables unit) ``` Raw op call (signature auth, no token): ```bash exec-sign.sh job.next '{"job_id": "heartbeat-20200101-000000-deadbeef"}' exec-sign.sh job.chain '{"from": "ops-audit-step2", "to": "ops-audit-step3"}' ``` Notes: - `job.trigger` vs existing `job.run`: `job.run` shells straight to `job-dispatch.py` and relays raw output; `job.trigger` goes through `box-ctl.py` (existence check, 300s bound, audit trail, JSON contract). Prefer `job.trigger` for agent-driven dispatches. - `job.next` job ids look like `-YYYYMMDD-HHMMSS-<8hex>`; with no `success` flag the box infers it from the last recorded result. ## 3. Job definition shape (`job.put`) The full schema is enforced by `box-ctl.py validate_job` (single copy); required fields: `name` (must match the argv name), `schedule` (`manual` or convertible cron), `agent` (fleet member), `prompt_template` (1โ€“4000 chars, no protocol literals, known `{placeholders}` only). `timeout` 60โ€“3600s, `on_failure` policy, optional `chain_next` (must exist) and `sidechat` / `dm_target` routing. `box-ctl.py` writes `jobs/.json` and commits (`Add/Update job via box-ctl`). ## 4. Safety notes (same posture as existing ops) - **Fixed argv, validated values.** Job names match `^[a-z0-9][a-z0-9-]{0,63}$`; trigger/chain/timer ops require the job file to exist; chain rejects self-links and cycles; timer ops require the unit to exist. All checks run before any side effect. - **Stdin plumbing.** `job.put` is the first op to pipe a request body to `box-ctl.py` stdin (the raw definition, not the envelope); the routing lives in one helper (`_stdin_body`) covered by unit tests. - **No deletes, no kills.** `job-delete` / `timer-delete` are reachable only over the operator SSH path, by explicit scope decision. - **Audited.** Every execution records identity + op + args hash; `box-ctl.py` additionally audits each mutation with its target. ## 5. Rollout notes - Restart `exec-constrained.py` after deploy for the new ops to appear in `GET /ops`. - `box-relay.sh` is served from bl (`GET /box`); agents re-fetch to get the `cron put|trigger|chain|next` and `timer stop|disable` commands. - Non-goals: deletes, `job-result` ingestion, `strat`/`loop` writes, approvals โ€” future expansions, same pattern.