Schedule a routine
A routine is a task the box starts on its own, on a timer. In this guide you write a small app with a schedule trigger, install it on the box, and check that it starts tasks. The schedule re-arms every time the box starts, so it survives restarts and power cuts.
Before you start
Section titled “Before you start”- A box installed on Ubuntu from a source checkout. See Install on Ubuntu. A box installed from an ISO has only the apps in its image, and a Server box has no shell to install one from, so use an Ubuntu install for your own app.
- A model key in the vault (
ANTHROPIC_API_KEYorFACTORY_API_KEY), because scheduled tasks run Droid with Claude by default. See Add a secret. - Basic TypeScript.
-
Make the app's folder in your checkout. The folder name is the app's name.
mkdir -p tracks/nightly -
Write
tracks/nightly/track.ts. This app starts one task every day at 02:00, box local time.import type { Track } from '../../core/contract.ts';import { schedule } from '../../core/triggers.ts';export default {name: 'nightly',triggers: [schedule({name: 'report',dailyAt: '02:00',task: () => ({title: 'Nightly notes report',prompt: 'Read notes/ and write report.md: what changed since yesterday, in five bullet points.',workspace: 'home',hosts: [],budgetCents: 25,source: 'schedule',}),}),],} satisfies Track;dailyAt: 'HH:MM'fires once a day at that time, on a 24-hour clock. UseeveryMsinstead for a fixed interval, for exampleeveryMs: 15 * 60_000for every 15 minutes.everyMsruns from 1 ms to about 24 days. A bad value stops the app from loading.workspace: 'home'is the workspace every box has. Put the files the routine reads there, or name another workspace.task()returns a task spec. Return nothing to skip a run, for example when there is no new work.hosts: []means the task reaches no website. Model calls go through the box. Add only the hosts the routine needs. See Network policy.budgetCents: a paid tool call that would take the task past this amount asks for your OK first. Model use counts toward the total but never pauses the task by itself.
-
Install the checkout on the box. The install script copies it to
/opt/boxand restarts the service, which loads every app at boot.sudo core/install.sh
Check it worked
Section titled “Check it worked”-
Look for the app in the service's startup line:
journalctl -u box.service -n 20 | grep 'box up'box up: 12 tasks in the log, tracks: anthropic, nightlyThe line lists only apps that are on. If
nightlyis missing, turn it on in App Center. If the app failed to load, the journal hastrack nightly not loaded:and the reason. A broken app never stops the box. -
To see a task without waiting until 02:00, set
everyMs: 60_000for a test run, install again, and wait a minute. Then list tasks:curl -s http://127.0.0.1:7700/api/tasks | grep -o '"title":"Nightly notes report"[^}]*'Change the schedule back when you are done.
Limits
Section titled “Limits”- A run that falls while the box is off is skipped, not caught up.
dailyAtarms the next occurrence after each start. everyMscounts from when the box started, not from a fixed clock time.dailyAtuses the box's local time zone.- Schedules live in app code. A screen for creating routines in the portal is not built.