Files
box/docs/BOX-JOBS-HTTPS.md
T

89 lines
4.2 KiB
Markdown
Raw Normal View History

# 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 <name> '<json-definition>' # create or update (see §3)
box cron trigger <name> # dispatch now (audited JSON)
box cron chain <from> <to> [--on-failure] # wire chain_next
box cron next <job-id> [--success|--fail] # dry-run: what dispatches next
box timer stop <name> # pause schedule (keeps unit)
box timer disable <name> # 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 `<name>-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/<name>.json`
and commits (`Add/Update job <name> 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.