Setu

Your binaries. Your servers. One control panel.

Setu 0.1.4: extra env variables and scheduled resets

Two operator features that let a Setu site run things its manifest did not plan for. The first users are the Vyasa sites on the Setu production server: the public demo (demo.vyasa.site) needs demo-mode variables and an hourly reset, and a site that installs packages outside the marketplace needs VYASA_PACKAGE_TRUSTED_KEYS.

1. Extra env variables

An extra variable is a stored env entry whose name the site's manifest does not declare. Nothing marks it in storage: whether a variable is extra is derived from the manifest of the site's current version, wherever it is needed. The format of sites.env does not change.

  • API. PUT /api/v1/sites/{id}/env gains two optional fields:

    The existing env map is unchanged; the three fields together must not be empty. Operator and above, audited as site.env_update.
    • extra, a map from name to { value, secret }, which adds or replaces extra variables;
    • remove, a list of extra names to delete.
  • Validation (in the API and again in the job):
    • The name matches ^[A-Z_][A-Z0-9_]{0,127}$.
    • It is not a manifest name. Those go through env, so the manifest always wins.
    • It is not reserved. The reserved names are PATH, HOME, USER, LOGNAME, SHELL, PWD, TMPDIR, LANG, NOTIFY_SOCKET, and anything starting with LD_, SYSTEMD_, LISTEN_, INVOCATION_, CREDENTIALS_ or SETU_.
    • The value has no control characters.
    • At most 64 extra variables per site.
    • remove names must be existing extras.
  • Storage. A secret extra is sealed (v1:), like a secret manifest value, and is masked in GET env and readable only through reveal. A non-secret extra is stored plain:. GET env entries gain extra: bool.
  • Job. env_update applies env, then extra, then remove. It stores the result, rewrites the env file and restarts a running site, as today. The payload carries every value sealed, as today.
  • Updates. After setu_manifest::merge, the update job re-appends every existing variable that is extra under the old manifest and not declared by the new one. If the new version starts declaring the name, the manifest rule takes over and the extra value is dropped. A rollback writes the old env, extras included.
  • UI. The site's Environment tab gets an Add variable form (name, value, Secret checkbox) and a Remove action on extra rows. Extra rows carry an "extra" chip.

2. Scheduled reset from a backup

An operator can choose one of a site's completed backups as its reset point and an interval. Setu then restores that backup on schedule.

  • Schema. Migration 0003_site_resets.sql adds a table site_resets with one row per site:

    A separate table keeps every existing sites query unchanged.
    • site_id, the primary key, references sites (id) with ON DELETE CASCADE;
    • backup_id references backups (id) with ON DELETE CASCADE;
    • every_minutes is checked to be between 15 and 10080;
    • last_reset_at records when the last reset was enqueued.
  • API.
    • GET /api/v1/sites/{id}/reset (viewer and above) returns { reset: { backup_id, every_minutes, last_reset_at, next_reset_at } | null }.
    • PUT with { backup_id, every_minutes } sets the schedule (operator and above). The backup must belong to the site and be completed. Setting it also sets last_reset_at = now, so the first reset comes one interval later.
    • DELETE (operator and above) clears the schedule, or returns 404 when there is none.
    • The changes are audited as site.reset_set and site.reset_clear.
  • Scheduler. On every tick, a site is reset when all of these hold:

    The scheduler then enqueues restore_site with { backup_id, reset: true } and sets last_reset_at = now. A missed reset happens once, on the next free tick; resets do not pile up.
    • its status is running;
    • a schedule is set and the backup still exists and is completed;
    • now >= last_reset_at + every_minutes;
    • it has no active job.
  • Restore job. With reset: true its backup step passes without taking a pre_restore backup; the step list stays the same, so a job resumed from its cursor still lines up. Taking a safety copy of a sandbox every hour would fill the disk with 30 days of pre-restore backups. Everything else is unchanged.
  • Retention. A backup that is any site's reset_backup_id is never pruned by the scheduler, whatever its kind. Deleting it by hand is refused with 409 ("backup is the site's reset point; clear the schedule first").
  • UI. The Backups tab gets Use as reset point on completed backups. It opens a dialog with an interval (every 15 min, 30 min, hourly, 6-hourly, daily, or weekly). A summary line at the top of the tab reads "Resets to <backup> every <interval>; next at <time>", with a Stop action.

Not in scope

Cron jobs inside the app (manifest v2, 0.4), env templating, and per-variable audit history.

Tests

  • Env: an extra is added, masked when secret, and revealed; it survives an update to a version that does not declare it; it is replaced by the rule of a version that does.
  • Env validation: a reserved name, a manifest name and a bad name are each refused in both the API and the job.
  • Remove: removing an extra rewrites the env file.
  • Scheduler: it resets when due, never while a job runs, and never twice for one missed window. The reset backup is never pruned, and its deletion is refused.
  • Restore: with reset there is no pre-restore backup.
  • e2e: not extended in 0.1.4. The env file write is the existing write_env path, which e2e already covers.