BetterIAM
Governance

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

JobCallCLISuggested cadence
Write buffered usageautomatic (every minute) and on demandnonenone
Invariant monitoriam.checkInvariants()monitor-invariantshourly
Role-mining snapshotroleMining.suggest as a token holdermine-roles --tenant IDweekly
Guardrail gate in CIinvariants.runcheck-invariantsevery deploy
  • Write buffered usage. With the accessUsage option on, the server counts which actions people use and writes the counts every minute (flushIntervalMs), or sooner when maxBuffered pairs wait. No schedule is needed, but call iam.flushAccessUsage() on shutdown so the last minute is not lost. See usage and role mining.
  • Invariant monitor. Evaluates every organization's and records invariant:broken or invariant:restored once per change, so a webhook on invariant:* 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-broken runs every invariant and exits with INVARIANTS_BROKEN when one is broken or cannot be evaluated. Run it after config-apply so a deploy that crosses a line fails.

Lifecycle jobs

The jobs behind privileged access and certifications:

JobCallCLISuggested cadence
Expire and purgeiam.purgeDeleted()purgenightly or more often
Package rules (birthright)iam.reconcilePackages()reconcileevery 15 minutes, after purge
Close due campaignsiam.closeOverdueCertifications()close-certificationsdaily
Owner digestiam.sendAccessDigest()digestdaily
Expiry remindersiam.sendExpiryReminders()reminddaily
Access report to a channelreports.access as a token holderreport --tenant IDnightly
Configuration drift checkconfig.plan as a token holderconfig-plan --fail-on-driftnightly and in CI
Deliver email and webhooksiam.auth.dispatchOutbox()outboxevery minute
Event subscribersiam.events.dispatch()none (in the subscribing process)every minute

What each one does:

  • Expire and purge disables identities past their expiresAt (recording identity: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-attention exits non-zero (RECONCILE_ATTENTION) when a rule needs a person.
  • Close due campaigns applies every created with autoClose once 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 digest and remind, which only queue their emails.
  • Event subscribers runs in-process subscribers, plugin hooks, and events.onEvent for newly recorded events. It belongs in the process that registers the subscribers. The CLI outbox command also runs this dispatch, but only for the plugins and events.onEvent in 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 as deployment-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 in BETTER_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

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_ID

To catch a job that stopped running, use better-iam doctor. Among other things, it reports:

  • expired bindings or memberships older than a day (purge is not running);
  • records due for sweep for 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 auditArchive is configured.

--strict makes it exit non-zero, which suits a health check. See observability.

Was this page helpful?

Better IAM is created by Sean Filimon

Last updated

On this page