Schedules and channels
Most runs start from an HTTP request. Triggers are the other two ways a run
begins: a schedule fires a prompt on a clock, and a channel fires on an
inbound event. Both run only while the agent is served (maiden serve), and both
share the same single runtime lock as the HTTP server — so a triggered run and a
request never overlap.
Schedules
Section titled “Schedules”A schedule fires a fixed prompt on a fixed interval. Create a file under
schedules/:
prompt = "summarize anything new since the last tick"every_seconds = 300| Key | Type | Description |
|---|---|---|
prompt |
string | The message sent on each fire. |
every_seconds |
integer | Seconds between fires. |
Each schedule runs on its own thread, named after the file. The prompt runs through the normal agent loop, so it can call tools and its history accumulates on that thread.
Channels
Section titled “Channels”A channel starts a run from an inbound event. There are seven kinds:
file-drop, telegram, slack, discord, github, linear and twilio.
Every credential is named, never written into the folder: a channel takes the name of an environment variable and resolves it at load time.
file-drop
Section titled “file-drop”maiden watches a directory, and each JSON file dropped into it becomes a message.
kind = "file-drop"dir = "/var/spool/my-agent-inbox"| Key | Type | Description |
|---|---|---|
kind |
string | Must be file-drop. |
dir |
string | Directory to watch for *.json files. |
Drop a file like {"message": "process order 1234"} into that directory, and
maiden runs the agent with that message. Each file is processed exactly once:
after the run, the file is moved into a .processed/ subdirectory, so a restart
never re-runs an already-handled message.
echo '{"message":"hello from a dropped file"}' > /var/spool/my-agent-inbox/msg.jsontelegram
Section titled “telegram”Long-polls the Bot API, so it needs no public URL and no port.
kind = "telegram"token_env = "TELEGRAM_BOT_TOKEN"allow_from = ["123456789"]| Key | Type | Description |
|---|---|---|
token_env |
string | Env var holding the bot token. |
allow_from |
string[] | Chat ids allowed to drive the agent. Empty or absent allows all. |
Webhook channels
Section titled “Webhook channels”slack, discord, github, linear and twilio receive events pushed over
HTTP. Each serves its own port — separate from maiden serve’s — and each
verifies the provider’s signature before doing anything with a request, so an
unsigned or forged event is rejected with a 401.
Give each channel a distinct port and put them behind a reverse proxy or tunnel that terminates TLS.
kind = "slack"port = 8091token_env = "SLACK_BOT_TOKEN"signing_secret_env = "SLACK_SIGNING_SECRET"| Kind | Keys beyond kind and port |
|---|---|
slack |
token_env, signing_secret_env |
discord |
public_key_env (Ed25519 public key, hex) |
github |
webhook_secret_env, installation_token_env |
linear |
access_token_env, webhook_secret_env |
twilio |
webhook_url, account_sid_env, auth_token_env, allow_from |
Notes worth knowing before you wire one up:
- Twilio’s
allow_fromis mandatory and deny-by-default. Unlike Telegram’s, an empty list denies every sender.webhook_urlmust be the exact public URL Twilio posts to, because it is part of what the signature covers. - GitHub takes a pre-resolved installation token. maiden does not mint one
from an App JWT, so whoever operates the agent rotates
installation_token_envbefore it expires. - Discord replies inline, as the interaction’s immediate response. That needs no bot token, but Discord’s acknowledgement window is about three seconds — a slow model turn can miss it.
A complete multi-channel agent lives in
examples/opsbot.
Running triggers
Section titled “Running triggers”Triggers start automatically with the server:
maiden serve my-agent# [trigger] schedule 'heartbeat' every 300s# [trigger] channel 'inbox' watching /var/spool/my-agent-inboxThey log each run’s outcome to stderr. Because everything shares one lock, a busy schedule or channel will queue behind in-flight work rather than run in parallel — see Execution model.