Files
box/docs/BOX-JOBS-HTTPS.md
operator 0065d11e97 feat(supervision): add choice watcher daemon, HTTPS spec docs, and test suites
- bin/muse_choice_watcher.py + systemd/muse-choices-reconcile.*: automatic choice answering and timer reconciliation
- bin/digest.py: fleet log and health summarization
- docs/BOX-*-HTTPS.md: comprehensive HTTPS execution contracts and API documentation
- docs/MUSE-CHOICES-POLICY.md & docs/SUPERVISION-SPEC.md: autonomous execution specs
- tests/test_*.py: unit test suites for HTTPS API, choice watcher, fleet heal, and swarm pruning
2026-10-07 00:25:46 +00:00

89 lines
4.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.