Skip to content

RaySpec v1.7.0

Choose a tag to compare

@iloveautomation iloveautomation released this 05 Aug 06:38
· 386 commits to main since this release
2aa47a1

The complete entry for this release is the ## [1.7.0] block in
CHANGELOG.md — Added,
Changed, Fixed, Documentation and Security. It is larger than a GitHub release body can
hold, so only its Upgrade notes are reproduced below, verbatim. Where they point at what is
documented "above", that is those changelog sections.

Upgrade notes

Everything below is documented in place above; this is the checklist. Nothing here applies to a
deployment that only authors specs and deploys them — the items are for embedders, for operators of an
existing database, and for clients written against the HTTP surface.

  • Three interfaces gained REQUIRED members, so an out-of-repository implementation stops
    typechecking until it grows them.
    ServerConfig (@rayspec/server) gains
    tenantBootstrapEnabled: boolean; the neutral DurableExecutor (@rayspec/platform) gains
    cancel(jobId: string): Promise<void>; CronSchedulerDeps (@rayspec/durable-dbos) gains
    tenantExists(tenantId: string): Promise<boolean>. A deployment that builds its config with
    loadServerConfig needs no change — it fills the new field from the environment; only code that
    constructs one of these objects itself is affected.

  • Apply migration 0010_journal_step_error_columns. Two additive nullable ADD COLUMNs on
    journal_steps; no table rewrite and no backfill.

  • A product deployment whose RAYSPEC_PRODUCT_TENANT_ID is malformed, or names no live org, now
    refuses to boot
    where it previously came up and waited for the org to appear. Settle the org before
    deploying — rayspec tenant ensure --org-id <uuid> --name <n> does it against DATABASE_URL with no
    running server. The cron tenant is deliberately unchanged.

  • A store read through the handler data facade with no explicit orderBy now comes back ordered
    id asc.
    A caller that passes its own orderBy is unaffected. Code that depended on the previous
    unordered result should state the order it wants.

  • A mounted static frontend answers non-content methods with 405 and an Allow: GET, HEAD, OPTIONS header, where such a request previously fell through to the SPA shell with 200. A client
    that sent one and read the shell as success will now see the refusal.

  • /health carries one more field and one more status value. A probe that matches the body exactly
    should accept frontend next to db, and status: "degraded" with 503 when a covered dependency
    is not ready.

  • errorClass has a new terminal value, cancelled. A client that enumerates error classes should
    accept it; a same-key retry replays it rather than starting a new run.

  • The two auth secrets are no longer mirrored into process.env. Code that read
    RAYSPEC_JWT_SIGNING_KEY or RAYSPEC_API_KEY_PEPPER back out of the environment after boot now finds
    nothing there, and a spawned child no longer inherits them.

  • A deployment booted from a *.product.yaml document starts running the daily system cleanup, and
    one half of it deletes data.
    The job did not run there before, so on such a deployment both halves
    are new behavior. The OIDC prune is ungated and starts hard-deleting expired oidc_models rows on
    the first scheduled instant after the upgrade; on a deployment that has been up for a while that
    first pass can clear a large accumulated backlog in one go — these are already-expired OAuth
    artifacts, but the delete is real. The GDPR tombstone purge runs only if RAYSPEC_GDPR_PURGE_ENABLED
    is exactly true: if you have that gate armed on a product deployment today you have been getting
    nothing from it, and after this upgrade you get the irreversible hard-delete the gate asks for — every
    user tombstone older than RAYSPEC_GDPR_RETENTION_DAYS (default 30), and every membership tombstone
    older than its own org's orgs.retention_days where that column is set, else that same default, goes
    on the first pass, across every org in the database rather than only the deployment tenant. Confirm
    that is what you want before upgrading; leaving the variable unset, or set to anything other than
    true, keeps the purge as a dry run that counts and deletes nothing, and that dry run is the only
    mitigation the shipped surface offers — the on-demand seam is in-process
    (BootedServer.runCleanupNow, for a host that embeds the server), so there is no command to run the
    pass once under supervision first. RAYSPEC_CLEANUP_SCHEDULE
    (default 0 3 * * *) now actually decides when that pass happens on this
    deployment shape — and because the expression is handed to the worker's scheduler as written, a value
    that scheduler cannot parse now aborts the boot of a product deployment that previously ignored it.
    The scheduler takes the standard 5-field crontab and the 6-field form that prepends a seconds field;
    shorthand such as @daily, a 4-field expression, or an out-of-range field is refused, and the refusal
    is the scheduler's own error, which names neither the variable nor the cleanup. Check the value before
    upgrading. Registering the job also adds one durable workflow to this deployment, which rotates the
    DBOS application version the product boot runs under: runs a pre-upgrade process enqueued or left in
    flight carry the old version and are neither dequeued nor recovered by the new one, so let the durable
    queues drain before restarting into this release (the applicationVersion that /recovery-scope
    reports changes with it). Deployments booted from a classic rayspec.yaml are unaffected: the job is
    wired there whenever the spec declares deployment.durableWorker: true and backends are wired, exactly
    as before — a classic spec that declares no durable worker never ran this job and still does not.

  • rayspec gen-handler now refuses a holes file carrying a key the hole shape it sits in does not
    declare
    , where it previously ignored the key and rendered anyway. This applies at the top level
    (per template) and inside each columns[] entry, fkRevalidate, and clampValues rule. A holes
    file that only uses declared keys is unaffected and renders identical bytes; one that carried a
    typo, a key of the other template, or a hand-added comment/metadata key at any level stops with
    ok: false and exit 1, and the error names the key (and the declared key it is a near-miss of).
    Fix the key or drop it — a build step that runs gen-handler will fail until it is settled, which
    is the point: that key was configuring nothing.

  • If you were reading your agent traces in the OpenAI dashboard from a rayspec deploy
    deployment, set RAYSPEC_AGENT_TRACING=openai before upgrading, or they stop arriving.
    That path
    now leaves the export off unless the variable is exactly openai; rayspec-serve and the local
    development wrapper are unchanged. Set nothing else to that variable — any value other than
    openai or off refuses the boot. Whichever way you leave it, the boot banner states the resolved
    posture, so check the Trace export: line on the first boot after upgrading.


Release artifacts

rayspec-release-identity.json is attached. It pins this release to the commit it was built from and
records, per package, the tarball integrity and the unpacked file-list digest, plus the schema digests,
the lockfile and dependency-SBOM digests, and the Node/pnpm requirement. The package tarballs it pins
are attached alongside it, so the manifest can be checked against the exact bytes it describes.

The manifest is unsigned: this repository has no release workflow, so keyless CI signing would mean
moving the release into CI. Its authenticity comes from the authenticated release it is attached to.

Verified before publishing: verified: 29 package(s), 0 failure(s).

Packages

The npm registry publish for this version follows separately.