Skip to content

v1.2.2

Choose a tag to compare

@iloveautomation iloveautomation released this 13 Jul 01:49
0bf03e2

Added

  • A server-stamped created_by actor column on every store row. Each row now
    records the principal that created it — user:<userId> for a JWT request,
    key:<apiKeyId> for an API-key request. It is stamped on create only (never
    re-stamped on update), returned in responses, and is not client-settable: it is a
    reserved column, so a created_by/createdBy field in a request body is rejected
    (400 VALIDATION_ERROR). It is filterable on a list route, so a caller can list
    only the rows it created. (A row created before this column existed carries a null
    created_by.)
  • Query power on the declarative store list op. A list route now accepts
    equality filters (?<column>=<value> on any declared column plus created_by,
    AND-combined — equality only, no ranges / OR / LIKE), single-column ordering
    (?order=<column>.asc|desc over non-nullable columns and the injected
    id/created_at; default id asc), and keyset pagination (?limit= in 1200,
    default 200, plus ?after=<opaque cursor>). A full page sets
    X-Result-Truncated: true and returns an X-Next-Cursor. Every filter, order, and
    cursor is folded through the tenant predicate, and an unknown query parameter is
    rejected (400). An offset-paged read or a filtered total count still drops to a
    handler route.
  • Idempotency-Key replay on store create. A create request carrying an
    Idempotency-Key header is deduplicated per tenant and per store: a repeat with
    the same key value replays the original row (200 with Idempotency-Replay: true,
    no duplicate row and no 409), regardless of the request body. A request without
    the header is never deduplicated. This is distinct from a unique: true column,
    whose duplicate value is a 409 CONFLICT rather than a replay.
  • Owner-gated org membership management. POST /v1/orgs/{orgId}/members adds a
    member by {email} — owner-only, via a live-membership permission check (a
    non-owner, or an API-key principal, is refused). An existing user is added
    idempotently as a member; a new email provisions an account and returns a
    oneTimePassword once in the owner's response (the core sends no mail — the owner
    conveys it out of band). GET /v1/orgs/{orgId}/members lists the org's members and
    is readable by any member. Accepted limitation: because the one-time password
    appears only for a newly provisioned account, the response reveals whether an email
    already has a platform account — accepted for the trusted single-node posture and
    closed by the out-of-band invite flow in the hardening layer (see SECURITY.md).
  • A shipped authoring skill, rayspec-author, guiding an assistant from a
    plain-language product brief to a validated spec and a deployed, curl-testable
    local backend, plus a gate:skill-drift build guard (in the deterministic CI lane)
    that fails if the skill drifts from the shipped grammar version, the CLI
    entrypoints, or the example specs it cites.
  • rayspec dev db --reset --yes. An opt-in, destructive local-dev reset that
    DROPs and re-CREATEs a clean, empty dev database (and drops the sibling
    <name>_dbos_sys durable-worker system database). It is gated on an explicit
    --yes; --reset without it refuses and touches nothing. The default dev db
    remains create-if-absent and never destructive.

Changed

  • store create/update bodies accept snake_case or camelCase column keys. A
    request may key each declared column by its snake_case declared name or its
    camelCase twin (the form the generated OpenAPI documents); both are accepted.
    Sending both variants of the same column in one body is rejected
    (400 VALIDATION_ERROR). Responses are always snake_case.
  • Newly minted API keys use an rk_ prefix (previously mk_); the key shape is
    rk_<public-prefix>.<secret>. Existing mk_ keys stay valid indefinitely — both
    prefixes are accepted.
  • Quieter boot. The benign NOTICE frames Postgres emits for each idempotent DDL
    guard in the migration chain (… already exists, skipping) are no longer printed,
    so a clean boot no longer prints a wall of messages that read like errors. A
    WARNING (or any higher severity) is still logged, and query error handling is
    unchanged.