Skip to content

core.cron

← Module catalog

soul-sidesoulstack.systemsystemroot required

Manage system cron jobs via /etc/cron.d/: one rule per file.

core.cron keeps the job file /etc/cron.d/<name> in the required state — a single line <schedule> <user> <command>. present is idempotent by a byte-for-byte comparison of the file's contents; cron picks up changes on its own, no daemon reload is needed. It covers only system-level jobs (user crontab via crontab -u is out of scope); requires root — the write goes to /etc/cron.d/.

Requirements

  • Rootrequired
  • Sidesoul-side
  • Collectionsoulstack.system
  • Categorysystem
Capabilitiesrun_as_rootfs_write_root

States

core.cron.present — The job file /etc/cron.d/<name> exists with the given schedule and command.

Changes when

The file did not exist or its contents differ from the target line <schedule> <user> <command> (byte-for-byte comparison).

Stays unchanged when

The file already contains the target line byte-for-byte.

Parameters

ParamTypeRequired / defaultDescription
namestringrequiredJob name (= file name in /etc/cron.d, [A-Za-z0-9_-]).
schedulestringrequiredCron schedule (5 fields), e.g. "0 3 * * *".
commandstringrequiredThe command run on schedule.
userstringoptionalUser to run as (default root).

Example — Nightly log rotation

- name: Schedule nightly log rotation
module: core.cron.present
params:
name: app-log-rotate
schedule: "0 3 * * *"
command: "/usr/local/bin/rotate-logs.sh"
user: root

Example — Backup under a dedicated user (privilege minimization)

- name: Schedule nightly backup
module: core.cron.present
params:
name: nightly-backup
schedule: "0 3 * * *"
command: "/usr/local/bin/backup.sh"
user: backup

Output

FieldTypeDescription
namestring
pathstring
installedtrue

Notes

  • The file is written in-process (os.WriteFile / os.Remove), without a shell and without subprocesses; the /etc/cron.d directory is created if needed (minimal containers may not have it). Cron picks up changes in /etc/cron.d/ automatically — no daemon reload is required.
  • The final contents are a single line <schedule> <user> <command>\n. present compares it with the file byte-for-byte: changed=true if the file did not exist or the contents differ.
  • The file is written with mode 0644 (cron strictly requires that a file in /etc/cron.d/ is not group/world-writable); the module does not change the owner — the assumption is that a production Soul runs as root.
  • MVP — only system-level jobs (one rule per file /etc/cron.d/<name>); user crontab (crontab -u) is deferred. The platform is Linux distributions whose cron reads /etc/cron.d/; on systems without this directory (FreeBSD, for example) it cannot be applied — this is controlled by the where: predicate in the scenario, not by the module itself.

Security & defaults

  • command is executed by the cron daemon on schedule as user (root by default) — the module's main invariant. schedule, user, and command are substituted into the file as-is, without syntax validation and without escaping: untrusted interpolation into command = deferred execution of someone else's code as user.
  • An additional vector: a value with \n in command or schedule will write extra cron lines into the file (the module does not parse the syntax). The source of command / schedule / user must be the Destiny/scenario author, not external input.
  • Only the job name is validated: name is limited to [A-Za-z0-9_-] (run-parts compatibility + a guard against path-injection — a dot/slash/.. are rejected before the write). There is no such check on schedule / command / user — the module trusts the task author.
  • required_capabilities [run_as_root, fs_write_root] — the write to /etc/cron.d/ goes outside /var/lib/soul-stack and requires UID 0; exec_subprocess is deliberately not declared (the module does not run external binaries). This is a declaration for soul-lint's static check, not a runtime privilege escalation.

See also