Viewing revision r1 of Scheduled Events — saved 2026-08-03 01:34 EDT .
“Initial seed content”
Jump to current version Full history

Scheduled Events

A built-in cron replacement. Sysop-only, Admin → Scheduled Events
(/admin/events/). Maintenance jobs run inside the same process as
the web app, on a one-minute tick, with results visible right in the
admin UI — no need to grep syslog to find out if last night's VACUUM
actually ran.

How it works

A background thread wakes every 60 seconds, loads every row with
Enabled checked, and fires any whose schedule says it's due. Each
row records last_run_at, last_status (ok/fail),
last_duration_ms, and the first 4KB of captured output — all shown
in the admin list. Editing or disabling a row takes effect on the
next tick (within a minute); no restart needed.

There's no per-event timeout enforced by the scheduler itself — a
handler that hangs blocks the next tick's events. Handlers that shell
out to something that could run long (like shell, below) enforce
their own timeout instead.

Creating an event

There's also a Run now button per row for testing without waiting
on the schedule — same code path either way, so output shown after a
manual run matches exactly what a scheduled fire would produce.

All schedule times are UTC, not the server's local timezone or
any caller's timezone — keep that in mind when picking a "quiet
hours" slot.

Built-in handlers

Handler key Label Params Notes
noop No-op (test) none Does nothing; returns ok. Use to confirm the scheduler thread is alive.
tw2_maint Trade Wars 2002 maint none Runs TW2002's headless daily maintenance (Cabal move, inactive-player sweep).
db_vacuum SQLite VACUUM none Reclaims free pages, defragments, refreshes planner stats. Skips (returns ok) on non-SQLite backends — Postgres autovacuums itself.
log_rotate Rotate large logs max_mb (default 50) Any logs/*.log over the threshold is renamed to .1 and a fresh empty file takes its place.
security_check Security update check none Scans apt list --upgradable and the venv's pip list --outdated, tags Ubuntu -security rows, writes a report consumed by Admin → Security. Always returns ok even on a non-Ubuntu box, so a missing apt doesn't permanently red-flag the row.
hub_generate_nodelist ANotherNetwork: generate nodelist none Publishes the ANotherNetwork nodelist into the ANN.FILES.NODELIST file area, replacing the prior copy. Only meaningful on the install designated as the ANotherNetwork hub (REGISTRY_MODE_ENABLED) — elsewhere it still runs harmlessly, publishing a nodelist with just the hub entry.
sync_wall_inbound InterBBS Wall: import inbound posts none Materializes new inbound InterBBS Wall echomail into local Wall posts. Auto-created when InterBBS Wall is enabled — you won't add this by hand.
sync_lastcallers_inbound InterBBS Last Callers: import inbound entries none Materializes new inbound echomail into local Last Callers entries. Auto-created when InterBBS Last Callers sharing is enabled.
sync_scores_inbound InterBBS Game Scores: import inbound scores none Materializes new inbound echomail into local game high scores. Auto-created when InterBBS Score Sharing is enabled.
shell Shell command command (required), timeout (default 60s) Runs an arbitrary command as the service user. No sudo — anything needing root privileges silently fails at that step. Output is captured as raw bytes and decoded with errors='replace', so non-UTF8 output (e.g. CP437 from a DOS program) doesn't crash the handler.

The three sync_*_inbound handlers aren't something you'd pick from a
blank slate — they appear already-created and enabled the moment you
turn on the matching InterBBS feature (Wall, Last Callers, or Game
Score sharing) on a network. Listed here so they aren't a mystery if
you spot one.

shell handler — worked example

Running a nightly TradeWars external-events script via dosemu2:

Params (JSON object):

{"command": "/opt/anetbbs/doors/dos/tw/event.sh", "timeout": 120}

event.sh:

#!/bin/bash
export TERM=linux
cd /opt/anetbbs/doors/dos/tw
/usr/libexec/dosemu2/dosemu2.bin -td EXTERN.EXE

export TERM=linux and -td (dumb-terminal mode) are required
because the scheduler runs the command with no controlling tty — a
normal door launch gets a real PTY from door_runner.py, but a
scheduled shell command doesn't. Without these, dosemu2 fails with
ERROR: TERM environment variable needs set. before it ever reaches
your DOS program.

Default events on a fresh install

Seeded once (idempotent — re-running the seeder skips handler keys
that already have a row):

Name Schedule Handler
TW2 daily maintenance Daily 03:30 UTC tw2_maint
Weekly SQLite VACUUM Weekly, Sunday 04:15 UTC db_vacuum
Rotate oversize logs Daily 04:45 UTC log_rotate (max_mb: 50)
Daily security update check Daily 04:00 UTC security_check

Any of these can be disabled, deleted, or rescheduled — they're
normal rows, not special-cased.

On installs with REGISTRY_MODE_ENABLED=true (the designated
ANotherNetwork hub), a fifth event is also seeded:

Name Schedule Handler
ANotherNetwork: weekly nodelist Weekly, Sunday 05:00 UTC hub_generate_nodelist

This one is skipped on regular (non-hub) installs — there's nothing
useful for it to publish there.

Troubleshooting

See also