Scheduling
The jobs, CLI commands, and cadences that keep governance and the access lifecycle running without manual work.
Much of governance happens between requests. Usage is written in batches, invariants are checked for new violations, due review campaigns close, package rules pick up directory changes, and expired accounts are disabled. Nothing does these things unless a scheduler runs them, and a missing job fails quietly: access that should have ended keeps working in reports, and alerts never fire.
This page lists each job, what it does, and how often to run it. Background jobs covers how to run a worker in each deployment model.
Governance jobs
| Job | Call | CLI | Suggested cadence |
|---|---|---|---|
| Write buffered usage | automatic (every minute) and on demand | none | none |
| Invariant monitor | iam.checkInvariants() | monitor-invariants | hourly |
| Role-mining snapshot | roleMining.suggest as a token holder | mine-roles --tenant ID | weekly |
| Guardrail gate in CI | invariants.run | check-invariants | every deploy |
- Write buffered usage. With the
accessUsageoption on, the server counts which actions people use and writes the counts every minute (flushIntervalMs), or sooner whenmaxBufferedpairs wait. No schedule is needed, but calliam.flushAccessUsage()on shutdown so the last minute is not lost. See usage and role mining. - Invariant monitor. Evaluates every organization's and records
invariant:brokenorinvariant:restoredonce per change, so a webhook oninvariant:*alerts without repeating itself. It catches what enforcement cannot, such as changes made by SCIM or other jobs. See change safety. - Role-mining snapshot. Prints role-mining suggestions and peer outliers as JSON, so you can review them and see whether the access model gets simpler over time.
- Guardrail gate.
check-invariants --fail-on-brokenruns every invariant and exits withINVARIANTS_BROKENwhen one is broken or cannot be evaluated. Run it afterconfig-applyso a deploy that crosses a line fails.
Lifecycle jobs
The jobs behind privileged access and certifications:
| Job | Call | CLI | Suggested cadence |
|---|---|---|---|
| Expire and purge | iam.purgeDeleted() | purge | nightly or more often |
| Package rules (birthright) | iam.reconcilePackages() | reconcile | every 15 minutes, after purge |
| Close due campaigns | iam.closeOverdueCertifications() | close-certifications | daily |
| Owner digest | iam.sendAccessDigest() | digest | daily |
| Expiry reminders | iam.sendExpiryReminders() | remind | daily |
| Access report to a channel | reports.access as a token holder | report --tenant ID | nightly |
| Configuration drift check | config.plan as a token holder | config-plan --fail-on-drift | nightly and in CI |
| Deliver email and webhooks | iam.auth.dispatchOutbox() | outbox | every minute |
| Event subscribers | iam.events.dispatch() | none (in the subscribing process) | every minute |
What each one does:
- Expire and purge disables identities past their
expiresAt(recordingidentity:expire). It deletes expired bindings, lapsed memberships, ended activations, and package assignments past their end, and marks stale access and package requests expired. It also removes organizations deleted more than 30 days ago (--retention-days). Expired access is refused at its next use regardless; this job decides how quickly statuses and reports catch up. See access lifecycle. - Package rules applies every package rule: it assigns
packages to identities that newly match and removes them from holders who stopped matching. It must be
scheduled, because SCIM provisioning, invitations, federated sign-in attributes, group membership changes, and
expiry reach the rules only through it.
--fail-on-attentionexits non-zero (RECONCILE_ATTENTION) when a rule needs a person. - Close due campaigns applies every created with
autoCloseonce its due date has passed, removing the bindings reviewers revoked. - Owner digest emails each organization's owners its access report when there is something to report, at most once per 20 hours.
- Expiry reminders emails each person whose account, bindings, memberships, or packages end within a week, so they can ask for an extension in time.
- Access report to a channel prints the report as JSON, for a chat channel or ticket.
- Configuration drift check exits non-zero (
CONFIG_DRIFT) when a tenant no longer matches its reviewed configuration file, so hand edits are noticed. - Deliver email and webhooks sends the messages that operations and the other jobs queued in the
, retrying failures with backoff. Run it after
digestandremind, which only queue their emails. - Event subscribers runs in-process subscribers, plugin hooks, and
events.onEventfor newly recorded events. It belongs in the process that registers the subscribers. The CLIoutboxcommand also runs this dispatch, but only for the plugins andevents.onEventin its configuration file. See events.
Two storage jobs belong in the same schedule: sweep (iam.sweepExpired()) deletes expired sessions, remembered
devices, relationship tuples, and old deliveries, and audit-archive (iam.archiveAudit()) copies new audit events
to your archive. See background jobs and the audit chain.
Credentials
Two kinds of jobs appear above:
- Deployment operations (
purge,sweep,reconcile,close-certifications,digest,remind,monitor-invariants,outbox,audit-archive) need no credential, only the configuration file. They act asdeployment-operator, and the ones that change access record their own audit events. - Token jobs (
report,mine-roles,check-invariants,config-plan,config-apply) act as the session or API key inBETTER_IAM_TOKEN, so each run is authorized and audited like the console. Give them a scoped API key of a service account with only the permissions they need (iam:analysis:read,iam:invariants:read,iam:config:read,iam:identities:read, and so on).
Example crontab
CONFIG=/etc/better-iam/better-iam.config.mjs
# Every minute: deliver email and webhooks
* * * * * better-iam outbox --config $CONFIG
# Every 15 minutes: package rules, after purge
*/15 * * * * better-iam reconcile --config $CONFIG --fail-on-attention
# Hourly: invariant monitor
0 * * * * better-iam monitor-invariants --config $CONFIG
# Nightly: expire and purge, sweep expired records, close due campaigns
0 2 * * * better-iam purge --config $CONFIG
15 2 * * * better-iam sweep --config $CONFIG
30 2 * * * better-iam close-certifications --config $CONFIG
# Mornings: email owners and people, then deliver
0 7 * * * better-iam digest --config $CONFIG && better-iam remind --config $CONFIG && better-iam outbox --config $CONFIG
# Weekly: role-mining snapshot (as a token holder)
0 6 * * 1 BETTER_IAM_TOKEN=... better-iam mine-roles --config $CONFIG --tenant TENANT_IDTo catch a job that stopped running, use better-iam doctor. Among other things, it reports:
- expired bindings or memberships older than a day (
purgeis not running); - records due for
sweepfor more than two days; - outbox messages, and audit hooks, waiting more than 15 minutes;
- audit events older than a day that are not archived, when
auditArchiveis configured.
--strict makes it exit non-zero, which suits a health check. See observability.
Better IAM is created by Sean Filimon
Last updated